基础字符串学习记录
记录一些简单的基础字符串知识 题目会有编号
等待重新排版233(并没有注意到格式和word不一样...在电脑上写完直接粘过来了....)
我好懵B啊...貌似不少东西都写错了..然后还能A??..明明随手造一组就挂掉的...数据怎么这么水..我还是重新写吧(之前代码全部删掉了...这坑真的要填不完了...)....
1.KMP
这个算法应该很基础了吧,最基本的应用:O(n+m)找出文本串在模板串中出现的位置。不过有很多关于nxt数组的神奇应用。
① 重复的密文
大概题意:有一个小写字符串鬼畜了,重复出现拼成了一个长串,现在给你长串,求原始串的最短长度是多少。长串长度是10^6
样例输入:
8
cabcabca
样例输出:
3
因为是重复出现...所以直接求出nxt数组,答案就是 len-1-nxt[len-1] (下标从0开始,我的定义可能跟其他人不一样...)
下面是代码:
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 #include<iostream> 5 #include<cmath> 6 char s[1000010]; 7 int nxt[1000010]; 8 using namespace std; 9 int main() 10 { 11 int i,j,len; 12 scanf("%d",&len); 13 scanf("%s",s); 14 nxt[0]=-1; 15 i=-1,j=0; 16 while(j<len){ 17 if(i==-1||s[i]==s[j]){ 18 i++,j++; 19 nxt[j]=i; 20 } 21 else i=nxt[i]; 22 } 23 printf("%d",len-1-nxt[len-1]); 24 }
② 阿里天池的新任务
大概题意:告诉你字符串的生成方法,先生成一个长串(作为模板串),之后给你一个短串(作为文本串),问短串在长串里出现了多少次。两个串长度均小于10^6
样例输入1:
13 2 5 4 9
AGG
样例输出1:
1
样例输入2:
103 51 0 40 60
ACTG
样例输出2:
5
这道题是KMP最模板的东西了吧...同时贴出poj3461代码(因为这道题生成规则实在是太麻烦...)
本题代码:
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 #include<iostream> 5 #include<cmath> 6 using namespace std; 7 char s1[1000010],s2[1000010]; 8 int w[1000010],nxt[1000010]; 9 int main() 10 { 11 int n,i,j,a,b,l,r,len,ans=0; 12 scanf("%d%d%d%d%d",&n,&a,&b,&l,&r); 13 scanf("%s",s2); 14 w[0]=b;len=strlen(s2); 15 for(i=1;i<n;i++) w[i]=(w[i-1]+a)%n; 16 for(i=0;i<n;i++){ 17 if(l<=w[i]&&w[i]<=r){ 18 if(w[i]%2==0) s1[i]='A'; 19 else s1[i]='T'; 20 } 21 else{ 22 if(w[i]%2==0) s1[i]='G'; 23 else s1[i]='C'; 24 } 25 } 26 nxt[0]=-1; 27 i=-1,j=0; 28 while(j<len){ 29 if(i==-1||s2[i]==s2[j]){ 30 i++,j++; 31 nxt[j]=i; 32 } 33 else i=nxt[i]; 34 } 35 i=j=0; 36 while(i<n){ 37 if(j==-1||s1[i]==s2[j]) i++,j++; 38 else j=nxt[j]; 39 if(j==len){ 40 ans++; 41 j=nxt[j]; 42 } 43 } 44 printf("%d",ans); 45 }
Poj3461代码:
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 #include<iostream> 5 #include<cmath> 6 using namespace std; 7 int len1,len2,ans,nxt[10010]; 8 char s1[1000010],s2[10010]; 9 void getnxt(){ 10 int p1=-1,p2=0; 11 nxt[0]=-1; 12 while(p2<len2){ 13 if(p1==-1||s2[p1]==s2[p2]){ 14 p1++;p2++; 15 nxt[p2]=p1; 16 } 17 else p1=nxt[p1]; 18 } 19 } 20 void kmp(){ 21 int p2=0,p1=0;ans=0; 22 while(p1<len1){ 23 if(p2==-1||s2[p2]==s1[p1]){p1++;p2++;} 24 else p2=nxt[p2]; 25 if(p2==len2){ 26 ans++; 27 p2=nxt[p2]; 28 } 29 } 30 } 31 int main() 32 { 33 int t; 34 scanf("%d",&t); 35 while(t--){ 36 scanf("%s%s",s2,s1); 37 len1=strlen(s1); 38 len2=strlen(s2); 39 getnxt(); 40 kmp(); 41 printf("%d\n",ans); 42 } 43 }
2.扩展KMP
这个算法并没有耐心学下去了,之前貌似学过...然而早就忘光了...至于题目可以用其他方法骗过去23333
③ 首尾相接
大概题意:给两个串S1 S2,找出一个字串使得这个字串是S1的前缀,并且是S2的后缀,输出最长满足条件的字串。S1 S2长度均为50000。
样例输入:
riemann
marjorie
样例输出:
rie 3
表示并不会EXKMP...貌似可以后缀数组...?说一下后缀数组的做法:把两个串拼在一起,直接跑出sa和h,找到sa[i]==len(前一个串的长度),然后答案就是从第二个串开始,总共长度有h[i+1]。
下面代码:
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 #include<iostream> 5 #include<cmath> 6 #define N 100010 7 using namespace std; 8 char s[N],s1[N]; 9 int sa[N],xx[N],yy[N],wv[N],num[N],rk[N],h[N],n,m; 10 bool cmp(int *r,int a,int b,int l){return r[a]==r[b]&&r[a+l]==r[b+l];} 11 void hou(){ 12 int i,j,p,k=0,*x=xx,*y=yy; 13 for(i=0;i<m;i++) num[i]=0; 14 for(i=0;i<n;i++) num[x[i]=s[i]]++; 15 for(i=1;i<m;i++) num[i]+=num[i-1]; 16 for(i=n-1;i>=0;i--) sa[--num[x[i]]]=i; 17 for(j=1,p=1;p<n;j*=2,m=p){ 18 p=0; 19 for(i=n-j;i<n;i++) y[p++]=i; 20 for(i=0;i<n;i++) if(sa[i]>=j) y[p++]=sa[i]-j; 21 for(i=0;i<n;i++) wv[i]=x[y[i]]; 22 for(i=0;i<m;i++) num[i]=0; 23 for(i=0;i<n;i++) num[wv[i]]++; 24 for(i=1;i<m;i++) num[i]+=num[i-1]; 25 for(i=n-1;i>=0;i--) sa[--num[wv[i]]]=y[i]; 26 swap(x,y),p=1,x[sa[0]]=0; 27 for(i=1;i<n;i++) x[sa[i]]=cmp(y,sa[i],sa[i-1],j)?p-1:p++; 28 } 29 for(i=0;i<n;i++) rk[sa[i]]=i; 30 for(i=0;i<n;h[rk[i++]]=k){for(k?k--:0,j=sa[rk[i]-1];s[i+k]==s[j+k];k++);} 31 return; 32 } 33 int main() 34 { 35 int i,j,len,mx;m=30; 36 scanf("%s%s",s1,s); 37 n=strlen(s); 38 len=strlen(s1); 39 s[n]='z'+1; 40 for(i=0;i<len;i++) s[n+i+1]=s1[i]; 41 len=n+1; 42 n=strlen(s); 43 for(i=0;i<n;i++) s[i]=s[i]-'a'+1; 44 n++;hou(); 45 for(i=0;i<n;i++) if(sa[i]==len) break; 46 for(j=len;j<len+h[i+1];j++) printf("%c",s[j]+'a'-1); 47 if(h[i+1]) printf(" %d",h[i+1]); 48 else printf("0"); 49 }
④ 旋转数字
大概题意:一串数字长度为n,可以把最后面的数移动到最前面(转圈),对于这样得到的n个数,求出 小于原数,等于原数,大于原数 的个数(需要去重)。比如,数123可以得到 312, 231, 123那么小于原数,等于原数,大于原数的各有一个。n<=100000
样例输入:
341
样例输出:
1 1 1
这道题还是可以用后缀数组做,先复制一倍直接跑出sa和h,然后从头到尾扫一遍,如果h==len就不计入答案(去重),在sa[i]==0之前的是小于原串的,之后的是就是大于原串的。
下面代码:
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 #include<iostream> 5 #include<cmath> 6 #define N 200010 7 using namespace std; 8 char s[N]; 9 int sa[N],xx[N],yy[N],wv[N],num[N],rk[N],h[N],n,m; 10 bool cmp(int *r,int a,int b,int l){return r[a]==r[b]&&r[a+l]==r[b+l];} 11 void hou(){ 12 int i,j,p,k=0,*x=xx,*y=yy; 13 for(i=0;i<m;i++) num[i]=0; 14 for(i=0;i<n;i++) num[x[i]=s[i]]++; 15 for(i=1;i<m;i++) num[i]+=num[i-1]; 16 for(i=n-1;i>=0;i--) sa[--num[x[i]]]=i; 17 for(j=1,p=1;p<n;j*=2,m=p){ 18 p=0; 19 for(i=n-j;i<n;i++) y[p++]=i; 20 for(i=0;i<n;i++) if(sa[i]>=j) y[p++]=sa[i]-j; 21 for(i=0;i<n;i++) wv[i]=x[y[i]]; 22 for(i=0;i<m;i++) num[i]=0; 23 for(i=0;i<n;i++) num[wv[i]]++; 24 for(i=1;i<m;i++) num[i]+=num[i-1]; 25 for(i=n-1;i>=0;i--) sa[--num[wv[i]]]=y[i]; 26 swap(x,y),p=1,x[sa[0]]=0; 27 for(i=1;i<n;i++) x[sa[i]]=cmp(y,sa[i],sa[i-1],j)?p-1:p++; 28 } 29 for(i=0;i<n;i++) rk[sa[i]]=i; 30 for(i=0;i<n;h[rk[i++]]=k){for(k?k--:0,j=sa[rk[i]-1];s[i+k]==s[j+k];k++);} 31 return; 32 } 33 int main() 34 { 35 int i,len,pd,a1,a2;m=12; 36 scanf("%s",s); 37 n=len=strlen(s); 38 for(i=0;i<n;i++) s[n+i]=s[i]; 39 n=strlen(s); 40 for(i=0;i<n;i++) s[i]=s[i]-'0'+1; 41 n++;hou(); 42 pd=a1=a2=0; 43 for(i=0;i<n;i++){ 44 if(h[i]>=len&&sa[i]!=0) continue; 45 if(sa[i]==0) pd=1; 46 else if(sa[i]<len){ 47 if(pd==0) a1++; 48 else a2++; 49 } 50 } 51 printf("%d 1 %d",a1,a2); 52 }
⑤ 匹配格式
大概题意:给定字符串S,找到字串E,使得S可以这样表示:EAEBE (A,B为任意字串),找出最长的E。S长度为10^6。
样例输入:
aaxoaaaaa
样例输出:
2
呃...出了点问题...代码貌似错了...这份代码是求了最长的出现3次的字串...(这都能A数据是有多水....)
错误的代码先贴上来(改一下二分里的判断方式就行了吧):
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 #include<iostream> 5 #include<cmath> 6 #define N 1000010 7 using namespace std; 8 char s[N]; 9 int sa[N],xx[N],yy[N],wv[N],num[N],rk[N],h[N],n,m; 10 bool cmp(int *r,int a,int b,int l){return r[a]==r[b]&&r[a+l]==r[b+l];} 11 void hou(){ 12 int i,j,p,k=0,*x=xx,*y=yy; 13 for(i=0;i<m;i++) num[i]=0; 14 for(i=0;i<n;i++) num[x[i]=s[i]]++; 15 for(i=1;i<m;i++) num[i]+=num[i-1]; 16 for(i=n-1;i>=0;i--) sa[--num[x[i]]]=i; 17 for(j=1,p=1;p<n;j*=2,m=p){ 18 p=0; 19 for(i=n-j;i<n;i++) y[p++]=i; 20 for(i=0;i<n;i++) if(sa[i]>=j) y[p++]=sa[i]-j; 21 for(i=0;i<n;i++) wv[i]=x[y[i]]; 22 for(i=0;i<m;i++) num[i]=0; 23 for(i=0;i<n;i++) num[wv[i]]++; 24 for(i=1;i<m;i++) num[i]+=num[i-1]; 25 for(i=n-1;i>=0;i--) sa[--num[wv[i]]]=y[i]; 26 swap(x,y),p=1,x[sa[0]]=0; 27 for(i=1;i<n;i++) x[sa[i]]=cmp(y,sa[i],sa[i-1],j)?p-1:p++; 28 } 29 for(i=0;i<n;i++) rk[sa[i]]=i; 30 for(i=0;i<n;h[rk[i++]]=k){for(k?k--:0,j=sa[rk[i]-1];s[i+k]==s[j+k];k++);} 31 } 32 bool check(int k){ 33 int i,j,mx,mi,p=0,*y=yy; 34 for(i=0;i<n;i++){ 35 if(h[i]<k){ 36 if(p&&mx-mi>=k+k+2) for(j=0;j<p;j++) if(mx-y[j]>=k+1&&y[j]-mi>=k+1) return true; 37 p=0; 38 y[p++]=sa[i]; 39 mi=mx=sa[i]; 40 } 41 else{ 42 mi=min(mi,sa[i]); 43 mx=max(mx,sa[i]); 44 y[p++]=sa[i]; 45 } 46 } 47 return false; 48 } 49 int main() 50 { 51 int i,l,r,mid;m=28; 52 scanf("%s",s); 53 n=strlen(s); 54 for(i=0;i<n;i++) s[i]=s[i]-'a'+1; 55 n++;hou(); 56 l=0,r=1000000; 57 while(l<r){ 58 mid=(l+r)/2; 59 if(check(mid)) l=mid+1; 60 else r=mid; 61 } 62 printf("%d",l-1); 63 }
3.Trie树
这个很简单吧......
⑥ 糟糕的BUG
大概题意:有233333个字符串(单个长度不超过10,总长度不超过466666),查找有没有字符串是另外的字符串的前缀
样例输入1:
3
abc
abcdef
cdef
样例输出1:
Bug!
样例输入2:
3
abc
bcd
cde
样例输出2:
Good Luck!
这么简单的题...代码:
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 #include<iostream> 5 #include<cmath> 6 using namespace std; 7 struct node{ 8 int nxt[26]; 9 bool cnt; 10 }tree[500010]; 11 int root=1,cnt=2,ans=0; 12 char s[20]; 13 void insert(){ 14 int pos,i,x,len=strlen(s); 15 pos=root; 16 for(i=0;i<len;i++){ 17 x=s[i]-'a'; 18 if(tree[pos].nxt[x]==0) tree[pos].nxt[x]=cnt++; 19 pos=tree[pos].nxt[x]; 20 } 21 tree[pos].cnt=true; 22 return; 23 } 24 void dfs(int pos){ 25 for(int i=0;i<26;i++){ 26 if(tree[pos].cnt&&tree[pos].nxt[i]){ans=1;return;} 27 if(tree[pos].nxt[i]) dfs(tree[pos].nxt[i]); 28 } 29 } 30 int main() 31 { 32 int n,i; 33 scanf("%d",&n); 34 for(i=1;i<=n;i++){ 35 scanf("%s",s); 36 insert(); 37 } 38 dfs(root); 39 if(ans==1) printf("Bug!"); 40 else printf("Good Luck!"); 41 }
2018.01.19 --PnCbf
update 2018.02.03(火车上补博客...晃来晃去的...)