虽然noip很少有单独把一些关于字符串的算法拉出来考,但是一定的练习也是必要的23333333333,至少要把板子打一遍吧2333333333333
这些题是一个裸到不能再裸的kmp,算是开始?233333
【hdu1711】Number Sequence
#include#include #include using namespace std; int ne[10010],n,m,t,ans; int a[1000010],b[10010]; inline void init() { int j=0;//j是指当前在匹配串的第几位,预处理next数组实际上很像next数组自己进行匹配 for(int i=2;i<=m;i++)//i是指当前在被匹配串的第几位 { while(j&&b[i]!=b[j+1])//如果当前这一位 j=ne[j];//向前面的与这一段匹配的子串跳转而去 if(b[i]==b[j+1])//如果当前这一位匹配 j++;//j就标记在这里 ne[i]=j;//当前这位的next就记录为j } } //kmp算法实际上是记录了之前的匹配信息,在这个基础上进行下一步匹配,感觉和记忆化搜索有点像233333 inline void kmp()//和刚才预处理类似 { int j=0;ans=0;//多加了个ans for(int i=1;i<=n;i++) { while(j&&b[j+1]!=a[i])//这里a成了被匹配串 j=ne[j]; if(a[i]==b[j+1]) j++; if(j==m)//因为这个题说找到最小的一个k,所以在这里停下 { ans=i-j+1;return; } } } int main() { scanf("%d",&t); while(t--) { memset(ne,0,sizeof(ne)); scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) scanf("%d",&a[i]); for(int i=1;i<=m;i++) scanf("%d",&b[i]); init();//预处理 kmp(); if(ans>0) printf("%d\n",ans); else printf("-1\n"); } }
【hdu1686】Oulipo
#include#include #include using namespace std; int t,lena,lenb,ne[10010],ans; char a[10010],b[1000010]; inline void init() { int j=0; for(int i=2;i<=lena;i++) { while(j&&a[i]!=a[j+1]) j=ne[j]; if(a[i]==a[j+1]) j++; ne[i]=j; } } inline void kmp() { int j=0;ans=0; for(int i=1;i<=lenb;i++) { while(j&&a[j+1]!=b[i]) j=ne[j]; if(b[i]==a[j+1]) j++; if(j==lena) ans++,j=ne[j];//和刚才那个题最大的变化就是这里,从直接返回到继续查找并答案加1 } } int main() { scanf("%d",&t); while(t--) { scanf("%s%s",a+1,b+1); memset(ne,0,sizeof(ne)); lena=strlen(a+1),lenb=strlen(b+1);//这种情况括号里面记得+1!!!!! init(); kmp(); printf("%d\n",ans); } }
【hdu2087】剪花布条
这个题还是换汤不换药……
#include#include #include using namespace std; int t,lena,lenb,ne[10010],ans; char a[10010],b[1000010]; inline void init() { int j=0; for(int i=2;i<=lenb;i++) { while(j&&b[i]!=b[j+1]) j=ne[j]; if(b[i]==b[j+1]) j++; ne[i]=j; } } inline void kmp() { int j=0;ans=0; for(int i=1;i<=lena;i++) { while(j&&b[j+1]!=a[i]) j=ne[j]; if(a[i]==b[j+1]) j++; if(j==lenb) ans++,j=0;//这个题就是把这里j清零就好,因为以前的部分不能用了 } } int main() { while(~scanf("%s",a+1)) { if(a[1]=='#') break; scanf("%s",b+1); memset(ne,0,sizeof(ne)); lena=strlen(a+1),lenb=strlen(b+1);//这种情况括号里面记得+1!!!!! init(); kmp(); printf("%d\n",ans); } }
【hdu3746】Cyclic Nacklace
严重吐槽这个题面描述不到位
#include#include #include using namespace std; int t,lena,ne[100010],ans,lenb; char a[100010]; inline void init()//只用求这个字符串的next数组就行了 { int j=0; for(int i=2;i<=lena;i++) { while(j&&a[i]!=a[j+1]) j=ne[j]; if(a[i]==a[j+1]) j++; ne[i]=j; } } inline void cha()//它好像数据要求如果现在恰好不循环,则认为现在只有一个循环节(无论他是否有已经有好几个小循环) //所以我一直wa,虽然他只说循环节必须多于一个,这个题好坑啊 { ans=0,lenb=1; int xun=lena-ne[lena];//寻找一下上一个循环节的位置 if(ne[lena]==0)//如果到最后还是0,则说明这整个是个循环节 ans=lena; else if(lena%xun==0)//如果恰好循环就答案为0 ans=0; else//否则就得凑 ans=xun-(lena%xun); } int main() { scanf("%d",&t); for(int i=1;i<=t;i++) { scanf("%s",a+1); memset(ne,0,sizeof(ne)); lena=strlen(a+1); init(); cha(); printf("%d\n",ans); } }