周末总结:
本周做的题的很少(虽然比上周多但也很少),很害怕这样的速度下去没什么提高,已经尽力在挤时间做题,很想
吐槽 聊聊Java,开学到现在代码敲了几乎快是一整学年C++的练习数了都是要提交的作业都是类似简单if判断for循环,实验课敲到吐打开4399最气人的是没flash没法玩。反正以后要努力提高自己的打字速度,争取早日提交实验作业。数据结构都是一些简单的大众知识点,对于实现还需要自己练,以后数据实验课不调试代码只做一遍先把简单的做完,因为有点浪费时间,不如课上仔细思考一下在实践。好啦,接下来整理一下最近题目学到的知识。
KMP算法
P2375动物园 题目大意:(动物园是个不错的地方) “KMP算法只能求出next数组。我现在希望求出一个更强大num数组一一对于字符串S的前i个字符构成的子串,既是它的后缀同时又是它的前缀,并且该后缀与该前缀不重叠,将这种字符串的数量记作num[i]” 输入: 3 aaaaa ab abcababc 输出: 36 1 32 分析: KMP假期有学但现在像是学了另一个KMP,发现next[]不同的地方存的数据含义也不同,数据结构存公共前后缀的长度,有人存公共前后缀相同的字符个数,可能根据需要来吧。
int getnext(char ch[],int length,int next[])
{
next[1]=0;
int i=1,j=0;
while(j<length)
{
if(j==0||ch[i]==ch[j]) next[++i]=++j;
else j=next[j];
}
}
手算KMP:(存公共前后缀相同的字符个数的版本,而该题不是) ----->j: 1 2 3 4 5 6 7 8 ---->p: a b a a b c a c next[j]: 0 0 1 2 2 3 1 2 next[j]:第j位字符前j-1位字符组成的子串的前后缀重合字符数+1 当j=1,规定next[1]=0; 当j=2,j前子串位=为“a”,next[2]=1; 当j=3,j前子串位=为“ab”,next[3]=1; 当j=4,j前子串位=为“aba”,next[4]=2; 当j=5,j前子串位=为“abaa”,next[5]=2; 当j=6,j前子串位=为“abaab”,next[6]=3; 当j=7,j前子串位=为“abaabc”,next[7]=1; 当j=8,j前子串位=为“abaabca”,next[8]=2;
以上是KMP知识 继续分析: 这个题并不是只让你求next[]数组,更高级啦,需要去除重叠的前后缀,并计算非重叠前后缀的数量。怎么去除重叠?答:只要让公共前缀长度不超过子串的一半即可。只要 jx2>i 就向前查找next【next【next【j】】】可以无限套娃寻找,直到 jx2<=i,就成功啦
#include <iostream>
#include <cstring>
#include <string>
using namespace std;
const int maxn=1000010;
const int mod=1e9+7;
int n,T;
int ne[maxn],num[maxn];
long long ans;
char s[maxn];
void getnext()
{
for(int i=2,j=0;i<=n;i++)
{
while(j&&s[i]!=s[j+1]) j=ne[j];
if(s[i]==s[j+1]) j++;
ne[i]=j;
num[i]=num[j]+1;
}
}
void getnum()
{
for(int i=2,j=0;i<=n;i++)
{
while(j&&s[i]!=s[j+1]) j=ne[j];
if(s[i]==s[j+1]) j++;
while((j<<1)>i) j=ne[j];
ans=(ans*(long long)(num[j]+1))%mod;
}
}
int main()
{
cin>>T;
while(T--)
{
ans=1;
num[1]=1;
cin>>s+1;
n=strlen(s+1);
getnext();
getnum();
cout<<ans<<endl;
}
return 0;
}
P3435
题目大意:求给定字符串所有前缀的最大周期长度之和。 输入: 8 babababa 输出: 24 分析: 所有前缀中最大周期的长度之和,关键是每个前缀的最大周期长度; 我们要找最大周期的子串长度,abcabcab,最大周期子串应该是abcabc长度为6,就是让其公共前后缀尽可能的短一点,这样就能保证子串较长,周期也就越长。这里我说的可能不是很明白,看例子: babababa,假设下标从1开始,一般next[8]=6,next[6]=4,next[4]=2; next[8]=6意思是,1-6和2-8是匹配的,但我们选择最短匹配,套娃找到next[8]=2,就是我们要找的答案:8-next[8]=6;
#include <iostream>
#include <cstring>
#include <string>
using namespace std;
const int maxn=1000010;
int ne[maxn],num[maxn];
long long ans;
char s[maxn];
int n;
int main()
{
cin>>n;
cin>>s+1;
for(int i=2,j=0; i<=n; i++)
{
while(j&&s[i]!=s[j+1]) j=ne[j];
if(s[i]==s[j+1]) j++;
ne[i]=j;
}
for(int i=2,j=2; i<=n; i++,j=i)
{
while(ne[j]!=0)
{
j=ne[j];
}
if(ne[i]!=0) ne[i]=j;
ans+=i-j;
}
cout<<ans<<endl;
return 0;
}
基础算法
P1483
题目大意: 两种操作第一种是输入三位数 1,x,y,将所有的 a(kx) (k*x<=n,k为正整数) 都加上y。 第二种操作是输入两位数2,j进行输出操作,输出a(j) 输入: 5 4 6 9 9 8 1 2 4 1 2 5 1 3 1 2 4 输出: 8 13 分析: 第一遍超时啦,直接遍历查找遍历相加,如果输入的操作有很多的话一定会超时,因为每一次操作又要来一次大的循环。发现操作二的输出操作只有10000,那我们不防从输出的 j 出发,找到j的约数,有多少约数是x就会相加多少个y。 code:使用到两个数组存方便许多,容易看懂
#include <iostream>
#include <cmath>
using namespace std;
int a[1000010];
int b[1000010];
int n,m;
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
while(m--)
{
int x;
cin>>x;
if(x==1)
{
int aa,bb;
cin>>aa>>bb;
b[aa]+=bb;
}else if(x==2)
{
int num,c;
cin>>c;
num=a[c];
for(int i=1;i<=sqrt(c);i++)
{
if(c%i==0)
{
num+=b[i];
if(i!=(c/i)) num+=b[c/i];
}
}
cout<<num<<endl;
}
}
return 0;
}
P1323删数问题 题目大意: (之前也做过类似的题目)一个集合中的元素有规定:1是集合中的元素,若 P 是集合的元素,则 2×P+1,4×P+5 也是集合的元素。取出最小的k个元素,从小到大组成一个多位数,再删除m个数位上的数是剩下的数字最大,编程输出删除前后的多位数。 分析: 题意很简单,题解代码操作很巧妙 继续分析: 首先我想的是排序但是没法实践,直接存不可以这时候就要换思路了,那就找小的值优先存,那也没办法保证大小关系,这时候就要想可自动排序又可弹出最小值的容器了,优先队列! code: 1优先队列的使用 2.将一个整数存到一个数组中,使得该数每个数位对应数组的一个下标,不是存放在一个空间,用到反向+取余 3.next实现删除操作,存放下一位的坐标,模拟链表
#include<iostream>
#include<queue>
#include<cstring>
#include<algorithm>
#define ll long long
#define H 0x7FFFFFFF
#define ull unsigned long long
using namespace std;
int a[30005],ans[5000010];
int nex[5000010];
int ii,jj;
priority_queue <int,vector<int>,greater<int> >q;
int main()
{
int l,m;
cin>>l>>m;
q.push(1);
memset(ans,0x3f,sizeof(ans));
while(1)
{
int x=q.top(),d=0;
q.pop();
q.push(2*x+1);
q.push(4*x+5);
a[++ii]=x;
while(x)
{
d=d*10+x%10;
x/=10;
}
while(d)
{
ans[++jj]=d%10;
d/=10;
}
if(ii>=l) break;
}
for(int i=1;i<=ii;i++) cout<<a[i];
cout<<endl;
for(int i=0;i<jj;i++)
{
nex[i]=i+1;
}
while(m)
{
int l=0;
while(ans[nex[l]]>=ans[nex[nex[l]]])
l=nex[l];
nex[l]=nex[nex[l]];
m--;
}
for(int i=0;nex[i];i=nex[i])
cout<<ans[nex[i]];
cout<<endl;
return 0;
}
P1381单词背诵 题目大意: 给出n个要背的单词,和文章中m个单词,输出文章中最多要背单词的最短的连续段的长度。 输入: 3 hot dog milk 5 hot dog dog milk hot 输出: 3 3 分析: 这个题还挺难的,思路好理解,利用哈希判断存到队列里的单词的个数,从队头处理如果队头元素,个数不止这一个就把它去掉。在存的过程中flag的作用是判断是否连续,不连续那么ans就会保持最小值。不说啦
#include<bits/stdc++.h>
#define ll long long
#define H 0x7FFFFFFF
#define ull unsigned long long
using namespace std;
const int maxn=1e5+7;
const ll inf=1e9+7;
char s[20];
map<ull,int>mp;
map<ull,int>mp2;
char a[maxn][15];
ull gethash(char *a)
{
ull sum=0;
for(int i=0;a[i]!='\0';i++)
{
sum=sum*131+a[i];
}
return sum&H;
}
int main()
{
ll n,m;
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>s;
mp[gethash(s)]=1;
}
cin>>m;
ll num=0;
for(int i=1;i<=m;i++)
{
cin>>a[i];
ull x=gethash(a[i]);
if(mp[x]==1)
{
num++;
mp[x]++;
}
}
cout<<num<<endl;
queue<int>q;
bool flag=false;
ll ans=inf;
for(int i=1;i<=m;i++)
{
if(mp[gethash(a[i])]==0)
{
mp2[gethash(a[i])]=inf;
}
flag=false;
if(mp2[gethash(a[i])]==0)
flag=true;
q.push(i);
mp2[gethash(a[i])]++;
int k=q.front();
while(mp2[gethash(a[k])]>1&&q.size())
{
mp2[gethash(a[k])]--;
q.pop();
k=q.front();
}
ll t=q.size();
if(flag)
{
ans=t;
}
else{
ans=min(t,ans);
}
}
cout<<ans<<endl;
return 0;
}
P1183多边形的面积 给出n个顶点的坐标,输出多边形的面积(简单但不会) 输入: 10 。。。 输出: 9 分析:(纯纯数学知识不懂) 用到行列式,二阶三阶行列式,先捺后撇计算。不会就搜索 从三角形对到多边形 坐标逆时针为正,若顺时针还应该加负号
#include<bits/stdc++.h>
using namespace std;
int main()
{
int n;
cin>>n;
double x[n+1],y[n+1];
for(int i=0;i<n;i++)
{
cin>>x[i]>>y[i];
}
double ans=0;
x[n]=x[0];
y[n]=y[0];
for(int i=0;i<n;i++)
{
ans+=0.5*(x[i]*y[i+1]-x[i+1]*y[i]);
}
cout<<ans<<endl;
return 0;
}
p1124 题目大意:不说了 字符串经过一系列排序,将形成的这些字符串的末尾输出形成一个新的字符串S’ 题目给出S’和S首字母在S’中的位置P 输入: 7 xelpame 7 输出: example (规律在纸上自己模拟就知道了)其实不用模拟就可以预测到前后字符串之间的对应关系,但是代码实践总会有这样那样的细节,首尾处理呀等等,纸上模拟吧~ 这里必须要用倒叙:但是这有一个问题,每次在S中找字符时候,S是无序的,所以找到S中的某个字符时可能并不能接上已经确定的答案字符串。所以在O中找,所以用逆序。
#include<bits/stdc++.h>
using namespace std;
int main()
{
int n,p;
cin>>n;
char o[10005];
char s[10005];
for(int i=1;i<=n;i++)
{
cin>>o[i];
s[i]=o[i];
}
sort(s+1,s+n+1);
cin>>p;
char ans[10005];
char c=o[p];
bool v[10005]={};
for(int i=1;i<=n;i++)
{
if(c==s[i])
{
c=o[i];
v[i]=true;
break;
}
}
int cnt=0;
ans[++cnt]=c;
for(int i=n;i>0;i--)
{
if(c==s[i]&&!v[i])
{
c=o[i];
v[i]=true;
ans[++cnt]=c;
i=n+1;
}
}
for(int i=cnt;i>0;i--)
{
cout<<ans[i];
}
return 0;
}
P2412查单词 这是个简单题,不分大小写的时候需要转化就是想说 找一个区间最大的,先排序再while查找就是想说
#include <iostream>
#include <string>
#include <cstring>
#include <algorithm>
using namespace std;
int n,m;
struct Na{
string c,s;
int id;
}na[50005];
bool cmp(Na x,Na y)
{
return x.s>y.s;
}
int main()
{
cin>>n>>m;
string ss;
for(int i=1;i<=n;i++)
{
cin>>ss;
int l=sizeof(ss);
na[i].c=ss;
for(int j=0;j<l;j++)
{
if(ss[j]<='Z'&&ss[j]>='A')
ss[j]=ss[j]-'A'+'a';
}
na[i].s=ss;
na[i].id=i;
}
sort(na+1,na+n+1,cmp);
int x,y;
for(int i=1;i<=m;i++)
{
cin>>x>>y;
int j=1;
while(1)
{
if(na[j].id>=x&&na[j].id<=y) break;
j++;
}
cout<<na[j].c<<endl;
}
return 0;
}
为什么总结博客有很多代码像是题解的博客呢,因为作者很菜 优秀,感觉这些代码们都很重要~~~
|