KMP:
Problem A.Number Sequence
d.求子串首次出现在主串中的位置
s.
c.
#include<iostream> #include<stdio.h> #include<string.h> using namespace std; #define MAXN 10005//字符串长度 int a[1000005]; int b[MAXN]; int _next[MAXN]; void GetNext(int t[],int M){//求next数组 int j,k,len; j=0;//从0开始,首先求_next[1] k=-1;//比较指针 _next[0]=-1;//初始值-1 //len=strlen(t); len=M; while(j<len){ if(k==-1||t[j]==t[k]){//指针到头了,或者相等 ++j; ++k; _next[j]=k;//此句可由优化替代 /*优化(求匹配位置时可用) if(t[j]!=t[k])_next[j]=k; else _next[j]=_next[k]; //*/ } else k=_next[k]; } } int KMPIndex(int s[],int t[],int N,int M){//求子串首次出现在主串中的位置 int i,j,lens,lent; i=j=0; //lens=strlen(s); //lent=strlen(t); lens=N; lent=M; while(i<lens&&j<lent){ if(j==-1||s[i]==t[j]){ ++i; ++j; } else j=_next[j]; } if(j>=lent)return i-lent+1; else return -1; } int main(){ int T; int N,M; scanf("%d",&T); while(T--){ scanf("%d%d",&N,&M); for(int i=0;i<N;++i){ scanf("%d",&a[i]); } for(int i=0;i<M;++i){ scanf("%d",&b[i]); } GetNext(b,M); printf("%d ",KMPIndex(a,b,N,M)); } return 0; }
Problem B.Oulipo
d.统计子串在主串中的出现次数,可重叠
s.
c.
#include<iostream> #include<stdio.h> #include<string.h> using namespace std; #define MAXN 10005//字符串长度 char W[MAXN]; char T[1000005]; int _next[MAXN]; void GetNext(char t[]){//求next数组 int j,k,len; j=0;//从0开始,首先求_next[1] k=-1;//比较指针 _next[0]=-1;//初始值-1 len=strlen(t); while(j<len){ if(k==-1||t[j]==t[k]){//指针到头了,或者相等 ++j; ++k; _next[j]=k;//此句可由优化替代 /*优化(求匹配位置时可用) if(t[j]!=t[k])_next[j]=k; else _next[j]=_next[k]; //*/ } else k=_next[k]; } } int KMPCount(char s[],char t[]){//统计子串在主串中的出现次数,可重叠 int i,j,lens,lent,cnt; i=j=0; lens=strlen(s); lent=strlen(t); cnt=0; while(i<lens){ if(j==-1||s[i]==t[j]){ ++i; ++j; } else j=_next[j]; if(j==lent)++cnt; } return cnt; } int main(){ int TT; scanf("%d",&TT); while(TT--){ scanf("%s",W); scanf("%s",T); GetNext(W); printf("%d ",KMPCount(T,W)); } return 0; }
Problem C.剪花布条
d.统计子串在主串中的出现次数,不可重叠
s.当j==lent时,直接让j=0;而不是j=_next[j];,就不重叠了~
c.
/* kmp模板 */ #include<iostream> #include<stdio.h> #include<string.h> using namespace std; #define MAXN 1024//字符串长度 int _next[MAXN]; void GetNext(char t[]){//求next数组 int j,k,len; j=0;//从0开始,首先求_next[1] k=-1;//比较指针 _next[0]=-1;//初始值-1 len=strlen(t); while(j<len){ if(k==-1||t[j]==t[k]){//指针到头了,或者相等 ++j; ++k; _next[j]=k;//此句可由优化替代 /*优化(求匹配位置时可用) if(t[j]!=t[k])_next[j]=k; else _next[j]=_next[k]; //*/ } else k=_next[k]; } } int KMPCount(char s[],char t[]){//统计子串在主串中的出现次数,不可重叠 int i,j,lens,lent,cnt; i=j=0; lens=strlen(s); lent=strlen(t); cnt=0; while(i<lens){ if(j==-1||s[i]==t[j]){ ++i; ++j; } else j=_next[j]; if(j==lent){ ++cnt; j=0;//不可重叠 } } return cnt; } int main(){ char str1[MAXN],str2[MAXN]; while(~scanf("%s",str1)){ if(str1[0]=='#')break; scanf("%s",str2); GetNext(str2); printf("%d ",KMPCount(str1,str2)); } return 0; }
Problem D.Cyclic Nacklace
d.给出一串字符串,可以在字符串的开头的结尾添加字符,求添加最少的字符,使这个字符串是循环的(例如:abcab 在结尾添加1个c变为 abcabc 既可)。
s.求出最小循环节,看总长能不能整除。
最小循环节(长度)=len-next[len];
c.
#include<iostream> #include<stdio.h> #include<string.h> using namespace std; #define MAXN 100005//字符串长度 char str[MAXN]; int _next[MAXN]; void GetNext(char t[]){//求next数组 int j,k,len; j=0;//从0开始,首先求_next[1] k=-1;//比较指针 _next[0]=-1;//初始值-1 len=strlen(t); while(j<len){ if(k==-1||t[j]==t[k]){//指针到头了,或者相等 ++j; ++k; _next[j]=k;//此句可由优化替代 /*优化(求匹配位置时可用) if(t[j]!=t[k])_next[j]=k; else _next[j]=_next[k]; //*/ } else k=_next[k]; } } int main(){ int T; int len; int len2;//最小循环节长度 scanf("%d",&T); while(T--){ scanf("%s",str); GetNext(str); len=strlen(str); len2=len-_next[len]; if(len2==len){ printf("%d ",len2); } else{ if(len%len2==0){ printf("%d ",0); } else{ printf("%d ",len2-(len%len2)); } } } return 0; }
Problem E.Period
d.统计单串中从某个位置以前有多少重复的串(即判断位置i之前的串是不是循环的串。如果是,输出位置i 和循环的次数)
s.最小循环节(长度)=len-next[len];
对每个位置求出最小循环节,然后判断前面的串是不是循环的即可。
c.
#include<iostream> #include<stdio.h> #include<string.h> using namespace std; #define MAXN 1000005//字符串长度 char str[MAXN]; int _next[MAXN]; void GetNext(char t[]){//求next数组 int j,k,len; j=0;//从0开始,首先求_next[1] k=-1;//比较指针 _next[0]=-1;//初始值-1 len=strlen(t); while(j<len){ if(k==-1||t[j]==t[k]){//指针到头了,或者相等 ++j; ++k; _next[j]=k;//此句可由优化替代 /*优化(求匹配位置时可用) if(t[j]!=t[k])_next[j]=k; else _next[j]=_next[k]; //*/ } else k=_next[k]; } } void KMPCount2(char t[]){//统计单串中从某个位置以前有多少重复的串 int i,lent,tmp; lent=strlen(t); for(i=2;i<=lent;++i){ tmp=i-_next[i]; if(i%tmp==0&&i/tmp>1) printf(" 位置:%d 个数:%d ",i,i/tmp); } } int main(){ int N; int c=0; int tmp;//最小循环节长度 while(~scanf("%d",&N)){ if(N==0)break; scanf("%s",str); GetNext(str); printf("Test case #%d ",++c); for(int i=2;i<=N;++i){ tmp=i-_next[i]; if( (i%tmp==0)&&(i/tmp>1) ){ printf("%d %d ",i,i/tmp); } } printf(" "); } return 0; }
Problem F.The Minimum Length
d.最小循环节
s.
c.
#include<iostream> #include<stdio.h> #include<string.h> using namespace std; #define MAXN 1000005//字符串长度 char str[MAXN]; int _next[MAXN]; void GetNext(char t[]){//求next数组 int j,k,len; j=0;//从0开始,首先求_next[1] k=-1;//比较指针 _next[0]=-1;//初始值-1 len=strlen(t); while(j<len){ if(k==-1||t[j]==t[k]){//指针到头了,或者相等 ++j; ++k; _next[j]=k;//此句可由优化替代 /*优化(求匹配位置时可用) if(t[j]!=t[k])_next[j]=k; else _next[j]=_next[k]; //*/ } else k=_next[k]; } } int main(){ int len; int len2;//最小循环节长度 while(~scanf("%s",str)){ len=strlen(str); GetNext(str); len2=len-_next[len]; printf("%d ",len2); } return 0; }
Problem G.Power Strings
d.求最大的循环次数
s.最小循环节
c.
#include<iostream> #include<stdio.h> #include<string.h> using namespace std; #define MAXN 1000005//字符串长度 char str[MAXN]; int _next[MAXN]; void GetNext(char t[]){//求next数组 int j,k,len; j=0;//从0开始,首先求_next[1] k=-1;//比较指针 _next[0]=-1;//初始值-1 len=strlen(t); while(j<len){ if(k==-1||t[j]==t[k]){//指针到头了,或者相等 ++j; ++k; _next[j]=k;//此句可由优化替代 /*优化(求匹配位置时可用) if(t[j]!=t[k])_next[j]=k; else _next[j]=_next[k]; //*/ } else k=_next[k]; } } int main(){ int len; int len2;//最小循环节长度 while(~scanf("%s",str)){ if(str[0]=='.')break; len=strlen(str); GetNext(str); len2=len-_next[len]; if(len%len2==0){ printf("%d ",len/len2); } else{ printf("1 "); } } return 0; }
Problem H.Seek the Name, Seek the Fame
d.给出一个字符串str,求出str中存在多少子串,使得这些子串既是str的前缀,又是str的后缀。从小到大依次输出这些子串的长度。
s.所以对于这道题,求出len处的next值,并递归的向下求出所有的next值,得到的就是答案。
c.
/* kmp模板 */ #include<iostream> #include<stdio.h> #include<string.h> using namespace std; #define MAXN 400005//字符串长度 int _next[MAXN]; void GetNext(char t[]){//求next数组 int j,k,len; j=0;//从0开始,首先求_next[1] k=-1;//比较指针 _next[0]=-1;//初始值-1 len=strlen(t); while(j<len){ if(k==-1||t[j]==t[k]){//指针到头了,或者相等 ++j; ++k; _next[j]=k;//此句可由优化替代 /*优化(求匹配位置时可用) if(t[j]!=t[k])_next[j]=k; else _next[j]=_next[k]; //*/ } else k=_next[k]; } } int main(){ char str[MAXN]; int sum[MAXN]; int k,len; while(~scanf("%s",str)){ GetNext(str); k=0; len=strlen(str); for(int i=len;_next[i]>0;i=_next[i]){ sum[k++]=_next[i]; } for(int i=k-1;i>=0;--i){ printf("%d ",sum[i]); } printf("%d ",len); } return 0; }
Problem I.Blue Jeans
d.求 N 个字符串的最长连续公共子串,N 范围是 10 ,每个串最长 60,所以可以暴力……
本来是没什么意思的,不过可以学习下string的几个函数
s.暴力。好像还能用后缀数组做,以后再看。
c.
#include<iostream> #include<stdio.h> #include<string> using namespace std; /* 涉及到string类的两个函数find和substr: 1、find函数 原型:size_t find ( const string& str, size_t pos = 0 ) const; 功能:查找子字符串第一次出现的位置。 参数说明:str为子字符串,pos为初始查找位置。 返回值:找到的话返回第一次出现的位置,否则返回string::npos 2、substr函数 原型:string substr ( size_t pos = 0, size_t n = npos ) const; 功能:获得子字符串。 参数说明:pos为起始位置(默认为0),n为结束位置(默认为npos) 返回值:子字符串 */ int main(){ int n; int m; string s[10]; scanf("%d",&n); while(n--){ scanf("%d",&m); for(int i=0;i<m;++i){ cin>>s[i]; } string ans=""; for(int i=60;i>=3;--i){//长度>=3 for(int j=0;j<=60-i;++j){ string tmp=s[0].substr(j,i);//从j开始取i长度的子串 bool flag=true; for(int k=1;k<m;++k){ if(s[k].find(tmp)==string::npos){ flag=false; break; } } if(flag){ if(ans==""){ ans=tmp; } else if(tmp<ans){ ans=tmp; } } } if(ans!="")break; } if(ans==""){ printf("no significant commonalities "); } else{ cout<<ans<<endl; } } return 0; }
Problem J.Simpsons’ Hidden Talents
d.两个字符串s1、s2,求s1和s2的最长的相同的s1前缀和s2后缀
s.先求s1的next数组,再求s2的next数组(即代码中_next2数组,此时不是自己与自己匹配,而是与s1匹配),最后看_next2[len2]即可(len2为串s2的长度)。
c.
#include<iostream> #include<stdio.h> #include<string.h> using namespace std; #define MAXN 50005//字符串长度 char s1[MAXN]; char s2[MAXN]; int _next[MAXN];//s1的next数组,s1与自己匹配 int _next2[MAXN];//s2的next数组,s2与s1匹配 void GetNext(char t[]){//求next数组 int j,k,len; j=0;//从0开始,首先求_next[1] k=-1;//比较指针 _next[0]=-1;//初始值-1 len=strlen(t); while(j<len){ if(k==-1||t[j]==t[k]){//指针到头了,或者相等 ++j; ++k; _next[j]=k;//此句可由优化替代 /*优化(求匹配位置时可用) if(t[j]!=t[k])_next[j]=k; else _next[j]=_next[k]; //*/ } else k=_next[k]; } } void GetNext2(char s[],char t[]){//s的前部与t匹配,求t相对于s的next数组(_next2[]) int j,k,len; //注意这几个初始化与上面不同 j=0;//从0开始,首先求_next[1] k=0;//比较指针 _next2[0]=0;//初始值0 len=strlen(t); while(j<len){ if(k==-1||t[j]==s[k]){//指针到头了,或者相等 ++j; ++k; _next2[j]=k;//此句可由优化替代 /*优化(求匹配位置时可用) if(t[j]!=t[k])_next[j]=k; else _next[j]=_next[k]; //*/ } else k=_next[k]; } } int main(){ while(~scanf("%s%s",s1,s2)){ GetNext(s1); GetNext2(s1,s2); int len2=strlen(s2); if(_next2[len2]==0){ printf("0 "); } else{ for(int i=0;i<_next2[len2];++i){ printf("%c",s1[i]); } printf(" %d ",_next2[len2]); } } return 0; }
Problem K.Count the string
d.统计所有前缀在串中出现的次数和
s.next数组,递推
c.
#include<iostream> #include<stdio.h> #include<string.h> using namespace std; #define MAXN 200005//字符串长度 #define MOD 10007 char s[MAXN]; int dp[MAXN]; int _next[MAXN]; void GetNext(char t[]){//求next数组 int j,k,len; j=0;//从0开始,首先求_next[1] k=-1;//比较指针 _next[0]=-1;//初始值-1 len=strlen(t); while(j<len){ if(k==-1||t[j]==t[k]){//指针到头了,或者相等 ++j; ++k; _next[j]=k;//此句可由优化替代 /*优化(求匹配位置时可用) if(t[j]!=t[k])_next[j]=k; else _next[j]=_next[k]; //*/ } else k=_next[k]; } } int main(){ int T; int n; int len; int ans; scanf("%d",&T); while(T--){ scanf("%d",&n); scanf("%s",s); GetNext(s); len=strlen(s); ans=0; dp[0]=0; for(int i=1;i<=len;++i){ dp[i]=dp[_next[i]]+1; dp[i]%=MOD; ans+=dp[i]; ans%=MOD; } printf("%d ",ans); } return 0; }
Problem L.Clairewd’s message(此题再看看,未完成)
d.
给出26个英文字母的加密表,明文中的'a'会转为加密表中的第一个字母,'b'转为第二个,...依次类推。
然后第二行是一个字符串(str1),形式是密文+明文,其中密文一定完整,而明文可能不完整(也可能没有)。
求出最短的完整的字符串(密文+明文)。
s.
1.用kmp来做:
首先肯定的是,给定的串中明文长度一定小于等于密文。也就是说明文长度小于等于总长的一半。
于是,取总长的后一半作为主串,然后把串反翻译一遍得到str2,然后用str2与str1的后一半进行匹配。首次把str1的后一半匹配完的位置即是给定的串中明文开始的位置。
因为是首次,所以保证了前面的密文长度最小,即总长度最小。
然后输出密文+明文,即可。
2.用扩展kmp来做:
//next[i]:x[i...m-1]与x[0...m-1]的最长公公前缀
//extend[i]:y[i...n-1]与x[0...m-1]的最长公共前缀
可以用extend数组,根据它的意义,str1作为y,str2作为x,当 i+extend[i]==len1时代表y中的从i到结尾均与x的开头匹配,如果此时i大于串长度的一半,则满足条件。
此时的i即为实际密文(明文)的长度,然后按要求输出答案即可。
c.kmp来做
/* kmp模板 */ #include<iostream> #include<stdio.h> #include<string.h> using namespace std; #define MAXN 100005//字符串长度 int _next[MAXN]; void GetNext(char t[]){//求next数组 int j,k,len; j=0;//从0开始,首先求_next[1] k=-1;//比较指针 _next[0]=-1;//初始值-1 len=strlen(t); while(j<len){ if(k==-1||t[j]==t[k]){//指针到头了,或者相等 ++j; ++k; _next[j]=k;//此句可由优化替代 /*优化(求匹配位置时可用) if(t[j]!=t[k])_next[j]=k; else _next[j]=_next[k]; //*/ } else k=_next[k]; } } int KMPIndex(char s[],char t[]){//s的后缀等于t的前缀的最大长度 int i,j,lens,lent; i=j=0; lens=strlen(s); lent=strlen(t); while(i<lens&&j<lent){ if(j==-1||s[i]==t[j]){ ++i; ++j; } else j=_next[j]; } //if(j>=lent)return i-lent; //else return -1; return j; } int main(){ char str[26],str1[MAXN],str2[MAXN]; char cstr[26];//密文->明文 int T; scanf("%d",&T); while(T--){ scanf("%s",str); scanf("%s",str1); for(int i=0;i<26;++i){//密文->明文 cstr[str[i]-'a']='a'+i; } int len1=strlen(str1); for(int i=0;i<len1;++i){ str2[i]=cstr[str1[i]-'a']; } str2[len1]='