涉及到的知识点:KMP,扩展KMP,Manacher算法,最小最大表示
牢记住:next[i]表示前i个字符所组成的字符串的最大前后缀匹配长度。
Number Sequence
http://acm.hust.edu.cn/vjudge/contest/view.action?cid=70325#problem/
判断一个字符串是否在另一个字符串中出现,如果出现,则输出最小的位置,否则输出-1.。
- #define rd(x) scanf("%d",&x)
- #define rd2(x,y) scanf("%d%d",&x,&y)
- #define rd3(x,y,z) scanf("%d%d%d",&x,&y,&z)
- using namespace std;
- typedef long long ll;
- const int maxn=1000010;
- const int maxm=10010;
- int a[maxn];
- int b[maxm];
- int lena,lenb;
- int nex[maxm];
-
- void kmp_pre()
- {
- int i,j;
- j=nex[0]=-1;
- i=0;
- while(i<lenb)
- {
- while(j!=-1&&b[i]!=b[j])
- j=nex[j];
- nex[++i]=++j;
- }
- }
-
- int KMP()
- {
- int i,j;
- kmp_pre();
- i=j=0;
- while(i<lena)
- {
- while(j!=-1&&a[i]!=b[j])
- j=nex[j];
- i++;j++;
- if(j>=lenb)
- return i-lenb+1;
- }
- return -1;
- }
-
- int main()
- {
- int t;rd(t);
- while(t--)
- {
- rd2(lena,lenb);
- for(int i=0;i<lena;i++)
- rd(a[i]);
- for(int i=0;i<lenb;i++)
- rd(b[i]);
- printf("%d\n",KMP());
- }
- return 0;
- }
Oulipo
http://acm.hust.edu.cn/vjudge/contest/view.action?cid=70325#problem/B
问主串中含有多少个模式串,可重叠。
- #define rd(x) scanf("%d",&x)
- #define rd2(x,y) scanf("%d%d",&x,&y)
- #define rd3(x,y,z) scanf("%d%d%d",&x,&y,&z)
- using namespace std;
- typedef long long ll;
- const int maxn=1000010;
- const int maxm=10010;
- char a[maxn];
- char b[maxm];
- int lena,lenb;
- int nex[maxm];
-
- void kmp_pre()
- {
- int i,j;
- j=nex[0]=-1;
- i=0;
- while(i<lenb)
- {
- while(j!=-1&&b[i]!=b[j])
- j=nex[j];
- nex[++i]=++j;
- }
- }
-
- int KMP_count()
- {
- int i,j;
- int ans=0;
- kmp_pre();
- i=j=0;
- while(i<lena)
- {
- while(j!=-1&&a[i]!=b[j])
- j=nex[j];
- i++,j++;
- if(j>=lenb)
- {
- ans++;
- j=nex[j];
- }
- }
- return ans;
- }
-
- int main()
- {
- int t;
- rd(t);
- while(t--)
- {
- scanf("%s",b);
- scanf("%s",a);
- lena=strlen(a);
- lenb=strlen(b);
- printf("%d\n",KMP_count());
- }
- return 0;
- }
剪花布条
http://acm.hust.edu.cn/vjudge/contest/view.action?cid=70325#problem/C
求主串中含有多少个模式串,不可重叠。
- #define rd(x) scanf("%d",&x)
- #define rd2(x,y) scanf("%d%d",&x,&y)
- #define rd3(x,y,z) scanf("%d%d%d",&x,&y,&z)
- using namespace std;
- typedef long long ll;
- const int maxn=1000010;
- const int maxm=10010;
- char a[maxn];
- char b[maxm];
- int lena,lenb;
- int nex[maxm];
-
- void kmp_pre()
- {
- int i,j;
- j=nex[0]=-1;
- i=0;
- while(i<lenb)
- {
- while(j!=-1&&b[i]!=b[j])
- j=nex[j];
- nex[++i]=++j;
- }
- }
-
- int KMP_count()
- {
- int i,j;
- int ans=0;
- kmp_pre();
- i=j=0;
- while(i<lena)
- {
- while(j!=-1&&a[i]!=b[j])
- j=nex[j];
- i++,j++;
- if(j>=lenb)
- {
- ans++;
- j=0;
- }
- }
- return ans;
- }
-
- int main()
- {
- while(scanf("%s",a)&&a[0]!='#')
- {
- scanf("%s",b);
- lena=strlen(a);
- lenb=strlen(b);
- printf("%d\n",KMP_count());
- }
- return 0;
- }
Cyclic Nacklace
http://acm.hust.edu.cn/vjudge/contest/view.action?cid=70325#problem/D
给定一个字符串,问最少在尾部加多少个字符,使得该字符串可以分为相等的几份。比如aaa,加0个,因为aaa是由三份a组成的,再比如abcde,那么得加5个,即abcdeabcde,
可以分为相等的两份。
原来字符串设cir=len-next[len] 意思是最小循环节的长度,如果它不等于本身的长度且lenb%cir等于0的话,就不用再加任何字符了,否则,再添加cir-lenb%cir个字符,把末尾的部分凑成一个循环节。
- #define rd(x) scanf("%d",&x)
- #define rd2(x,y) scanf("%d%d",&x,&y)
- #define rd3(x,y,z) scanf("%d%d%d",&x,&y,&z)
- using namespace std;
- typedef long long ll;
- const int maxm=100010;
- char b[maxm];
- int lenb;
- int nex[maxm];
-
- void kmp_pre()
- {
- int i,j;
- j=nex[0]=-1;
- i=0;
- while(i<lenb)
- {
- while(j!=-1&&b[i]!=b[j])
- j=nex[j];
- nex[++i]=++j;
- }
- }
-
- int main()
- {
- int t;
- rd(t);
- while(t--)
- {
- scanf("%s",b);
- lenb=strlen(b);
- kmp_pre();
- int cir=lenb-nex[lenb];
-
- if(cir!=lenb&&lenb%cir==0)
- printf("0\n");
- else
- printf("%d\n",cir-lenb%cir);
- }
- return 0;
- }
Period
http://acm.hust.edu.cn/vjudge/contest/view.action?cid=70325#problem/E
给定一个长度为n的字符串,求它每个前缀的最短循环节,换句话说,对于每个i(2<=i<=n),求一个最大的整数K(如果K存在),使得S的前i个字符组成的前缀是某个字符串重复K次得到。输出存在K的i和对应的K。
最小循环节为i-next[i], 如果i%(i-next[i])==0且next[i]!=0的话,那么前i个字符组成的前缀一定存在循环节,循环节的个数为 i/(i-next[i])。
- #define rd(x) scanf("%d",&x)
- #define rd2(x,y) scanf("%d%d",&x,&y)
- #define rd3(x,y,z) scanf("%d%d%d",&x,&y,&z)
- using namespace std;
- typedef long long ll;
- const int maxn=1000010;
- int lenb;
- char b[maxn];
- int nex[maxn];
-
- void kmp_pre()
- {
- int i,j;
- j=nex[0]=-1;
- i=0;
- while(i<lenb)
- {
- while(j!=-1&&b[i]!=b[j])
- j=nex[j];
- nex[++i]=++j;
- }
- }
-
- int main()
- {
- int c=1;
- while(rd(lenb)!=EOF&&lenb)
- {
- scanf("%s",b);
- kmp_pre();
- printf("Test case #%d\n",c++);
- for(int i=2;i<=lenb;i++)
- {
- int cha=i-nex[i];
- if(nex[i]!=0&&i%cha==0)
- printf("%d %d\n",i,i/cha);
- }
- printf("\n");
- }
- return 0;
- }
Power Strings
http://acm.hust.edu.cn/vjudge/contest/view.action?cid=70325#problem/G
给出一个字符串,某个子串链接n次产生的,求最大的n。也就是求最小的循环节
如 aabaabaabaab,输出3
如 aabaaba ,输出7
- #define rd(x) scanf("%d",&x)
- #define rd2(x,y) scanf("%d%d",&x,&y)
- #define rd3(x,y,z) scanf("%d%d%d",&x,&y,&z)
- using namespace std;
- typedef long long ll;
- const int maxn=1010010;
- int lenb;
- char b[maxn];
- int nex[maxn];
-
- void kmp_pre()
- {
- int i,j;
- j=nex[0]=-1;
- i=0;
- while(i<lenb)
- {
- while(j!=-1&&b[i]!=b[j])
- j=nex[j];
- nex[++i]=++j;
- }
- }
-
- int main()
- {
- while(scanf("%s",b)==1&&b[0]!='.')
- {
- lenb=strlen(b);
- kmp_pre();
- int k=lenb-nex[lenb];
- if(lenb%k==0)
- printf("%d\n",lenb/k);
- else
- printf("1\n");
- }
- return 0;
- }
Seek the Name, Seek the Fame
http://acm.hust.edu.cn/vjudge/contest/view.action?cid=70325#problem/H
给定一个字符串,有多少个前缀等于后缀,输出前缀的位置.
比如
ababcababababcabab 输出2 4 9 18
aaaaa 输出1 2 3 4 5
abaaba 输出 1 3 6
就是next[]的回溯。
以abaaba为例
位置i 0 1 2 3 4 5 6
第几个 1 2 3 4 5 6 7
字符 a b a a b a
next[i] -1 0 0 1 1 2 3
长度为6 ,注意next[6]的值,它代表的意思是前6个字母的前缀和后缀最长公共序列的长度,也就是aba
长度为6的前缀肯定是符合题意的,前缀等于后缀了.
回溯next[6]=3,也就是第6个字母往前3个字母组成的后缀(aba)和前三个字母组成的前缀
aba是相等的。再回溯next[3]=1,也就是第三个字母往前1个字母(包括第三个字母,也就是
a),和前面1个字母组成的前缀是相等的。因为 aba aba相等,那么前面aba的后缀a也就是后面aba
的后缀。
next[1]=0,结束。
- #define rd(x) scanf("%d",&x)
- #define rd2(x,y) scanf("%d%d",&x,&y)
- #define rd3(x,y,z) scanf("%d%d%d",&x,&y,&z)
- using namespace std;
- typedef long long ll;
- const int maxn=400010;
- int lenb;
- char b[maxn];
- int nex[maxn];
-
- void kmp_pre()
- {
- int i,j;
- j=nex[0]=-1;
- i=0;
- while(i<lenb)
- {
- while(j!=-1&&b[i]!=b[j])
- j=nex[j];
- nex[++i]=++j;
- }
- }
- int ans[maxn];
-
-
- int main()
- {
- while(gets(b))
- {
- lenb=strlen(b);
- kmp_pre();
- int c=0;
- for(int i=lenb;i!=0;i=nex[i])
- {
- ans[c++]=i;
- }
- for(int i=c-1;i>=1;i--)
- cout<<ans[i]<<" ";
- cout<<ans[0]<<endl;
- }
- return 0;
- }
Simpsons’ Hidden Talents
http://acm.hust.edu.cn/vjudge/contest/view.action?cid=70325#problem/J
给定两个字符串s1,s2,求最长的字符串,使得该字符串是s1的前缀,是s2的后缀,输出该字符串,以及长度。
将两个字符串连接s1+s2,求next[]数组,然后从next[ (s1+s2).length() ]开始向前回溯,next[i]表示前i个字符所组成的字符串的最大前后缀匹配长度,注意回溯过程中长度p要小于等于s1的长度且小于等于s2的长度,要求最长,满足该条件,退出即可。
- #define rd(x) scanf("%d",&x)
- #define rd2(x,y) scanf("%d%d",&x,&y)
- #define rd3(x,y,z) scanf("%d%d%d",&x,&y,&z)
- using namespace std;
- typedef long long ll;
- string s1,s2;
- int lenb;
- const int maxn=50010*2;
- int nex[maxn];
- void kmp_pre(string b)
- {
- int i,j;
- j=nex[0]=-1;
- i=0;
- while(i<lenb)
- {
- while(j!=-1&&b[i]!=b[j])
- j=nex[j];
- nex[++i]=++j;
- }
- }
-
-
- int main()
- {
- while(cin>>s1>>s2)
- {
- string s=s1+s2;
- int len1=s1.length();
- int len2=s2.length();
- lenb=s.length();
- kmp_pre(s);
- int p=nex[lenb];
- while(p>len1||p>len2)
- p=nex[p];
- if(p!=0)
- {
- for(int i=0;i<p;i++)
- cout<<s1[i];
- cout<<" "<<p<<endl;
- }
- else
- cout<<0<<endl;
- }
- return 0;
- }
Count the string
http://acm.hust.edu.cn/vjudge/contest/view.action?cid=70325#problem/K
给定一个字符串,求所有的前缀在字符串中出现的次数和.
比如 abab
前缀a 出现2次
ab 出现2次
aba出现1次 abab出现1一次
dp[i]表示前i(i从1开始)个字符组成的字符串中,有多少个前缀是以第i个字符结尾的
比如前i个字符为aba,那么dp[i]=2,因为aba前缀中有 a 和aba是以a结尾的
next[i]表示前i个字符中前缀和后缀的最大匹配长度,比如前i个字符是abab那么next[4]=
那么 dp[i]=dp[next[i]]+1,为什么呢? 假如 ababab,dp[6]=dp[4]+1,前六个字符包括前4个字符,
dp[4]是前四个字符所有前缀中以第四个字符结尾的个数,第6个字符又等于第4个字符,所以再加上它
本身ababab,就是dp[6].
- #define rd(x) scanf("%d",&x)
- #define rd2(x,y) scanf("%d%d",&x,&y)
- #define rd3(x,y,z) scanf("%d%d%d",&x,&y,&z)
- using namespace std;
- typedef long long ll;
- int lenb;
- const int maxn=200010;
- char b[maxn];
- int nex[maxn];
- const int mod=10007;
- int ans;
- int dp[maxn];
-
- void kmp_pre()
- {
- int i,j;
- j=nex[0]=-1;
- i=0;
- while(i<lenb)
- {
- while(j!=-1&&b[i]!=b[j])
- j=nex[j];
- nex[++i]=++j;
- }
- }
-
- int main()
- {
- int t;rd(t);
- while(t--)
- {
- rd(lenb);
- scanf("%s",b);
- kmp_pre();
- memset(dp,0,sizeof(dp));
- ans=0;
- for(int i=1;i<=lenb;i++)
- {
- dp[i]=dp[nex[i]]+1;
- if(dp[i]>=mod)
- dp[i]%=mod;
- ans+=dp[i];
- if(ans>=mod)
- ans%=mod;
- }
- printf("%d\n",ans);
-
- }
- return 0;
- }
Clairewd’s message(这题参考的网上的....有些地方没明白)
http://acm.hust.edu.cn/vjudge/contest/view.action?cid=70325#problem/L
S主串,T模式串
定义母串S,和字串T,设S的长度为n,T的长度为m,求S的每一个后缀与T的最长公共前缀,
也就是说,设extend数组,extend[i]表示T与S[i,n-1]的最长公共前缀,要求出所有extend[i](0<=i<n)。
设辅助数组next[i]表示T[i,m-1]和T的最长公共前缀长度
如果有一个位置extend[i]=m,则表示T在S中出现,而且是在位置i出现,这就是标准的KMP问题,
所以说拓展kmp是对KMP算法的扩展,所以一般将它称为扩展KMP算法。
资料:http://blog.csdn.net/dyx404514/article/details/41831947
本题:
给定一个转换表,也就是明文对应的暗文
再给你一个串, 里面是暗文+明文,明文不一定完整,甚至没有
让你以最短的形式补全这个串,使得前半部分为暗文,后半部分为对应的明文
原来串: 暗文+明文(明文可能没有)
译码过去: 明文+暗文
要求补齐的明文最短,那么就是求原来串的所有后缀与后来串的最长公共前缀。
因为原来串中的明文越长,需要补全的明文就越短。
- #define rd(x) scanf("%d",&x)
- #define rd2(x,y) scanf("%d%d",&x,&y)
- #define rd3(x,y,z) scanf("%d%d%d",&x,&y,&z)
- using namespace std;
- typedef long long ll;
- const int maxn=100010;
- int nex[maxn];
- int extend[maxn];
- char s[maxn];
- char t[maxn];
- int lens,lent;
-
- void pre_kmp()
- {
- nex[0]=lent;
- int j=0;
- while(j+1<lent&&t[j]==t[j+1])
- j++;
- nex[1]=j;
- int k=1;
- for(int i=2;i<lent;i++)
- {
- int p=nex[k]+k-1;
- int L=nex[i-k];
- if(i+L<p+1)
- nex[i]=L;
- else
- {
- j=max(0,p-i+1);
- while(i+j<lent&&t[i+j]==t[j])
- j++;
- nex[i]=j;
- k=i;
- }
- }
- }
-
- void EKMP()
- {
- pre_kmp();
- int j=0;
- while(j<lens&&j<lent&&t[j]==s[j])
- j++;
- extend[0]=j;
- int k=0;
- for(int i=1;i<lens;i++)
- {
- int p=extend[k]+k-1;
- int L=nex[i-k];
- if(i+L<p+1) extend[i]=L;
- else
- {
- j=max(0,p-i+1);
- while(i+j<lens&&j<lent&&s[i+j]==t[j])
- j++;
- extend[i]=j;
- k=i;
- }
- }
- }
-
- char table[maxn];
- map<char,char>mp;
-
- int main()
- {
- int cas;rd(cas);
- while(cas--)
- {
- scanf("%s",table);
- scanf("%s",s);
- lens=strlen(s);
- for(int i=0;i<26;i++)
- mp[table[i]]='a'+i;
- for(int i=0;i<lens;i++)
- t[i]=mp[s[i]];
- lent=strlen(t);
- s[lens]=0;
- EKMP();
-
-
- int p;
- for(p=0;p<lens;p++)
- {
- if(p+extend[p]>=lens&&p>=extend[p])
- break;
- }
- for(int i=0;i<p;i++)
- printf("%c",s[i]);
- for(int i=0;i<p;i++)
- printf("%c",mp[s[i]]);
- printf("\n");
- }
- return 0;
- }
Substrings
http://acm.hust.edu.cn/vjudge/contest/view.action?cid=70325#problem/M
给定n个字符串,求这n个字符串的最长公共子串,这个公共子串可以是正着的,也可以是反着的。
找到长度最小的字符串,然后枚举子串,再去其它串里查找就可以了。
- #define rd(x) scanf("%d",&x)
- #define rd2(x,y) scanf("%d%d",&x,&y)
- #define rd3(x,y,z) scanf("%d%d%d",&x,&y,&z)
- using namespace std;
- typedef long long ll;
- const int maxn=110;
- int n;
- string str[maxn];
- int minp,p;
-
- int main()
- {
- int t;
- rd(t);
- while(t--)
- {
- rd(n);
- minp=10000;
- for(int i=1;i<=n;i++)
- {
- cin>>str[i];
- int tp=str[i].length();
- if(minp>tp)
- {
- minp=tp;
- p=i;
- }
- }
-
- bool ok;
- int ans=0;
- for(int i=0;i<minp;i++)
- {
- for(int j=1;i+j<=minp;j++)
- {
- string s1=str[p].substr(i,j);
- string s2=s1;
- reverse(s2.begin(),s2.end());
-
- ok=1;
- for(int k=1;k<=n;k++)
- {
- if(str[k].find(s1,0)==-1&&str[k].find(s2,0)==-1)
- {
- ok=0;
- break;
- }
- }
- if(ok)
- {
- if(ans<j)
- ans=j;
- }
- }
- }
- printf("%d\n",ans);
- }
- return 0;
- }
Corporate Identity
http://acm.hust.edu.cn/vjudge/contest/view.action?cid=70325#problem/N
求n个字符串的最长公共子串。如果长度相等,则输出字典序最小的那个。
找出长度最小的字符串,然后枚举该串的子串,然后进行Kmp就可以了。
- #define rd(x) scanf("%d",&x)
- #define rd2(x,y) scanf("%d%d",&x,&y)
- #define rd3(x,y,z) scanf("%d%d%d",&x,&y,&z)
- using namespace std;
- typedef long long ll;
- const int maxn=210;
- char str[4010][maxn];
- int nex[maxn];
-
- void kmp_pre(char b[],int lenb)
- {
- int i,j;
- j=nex[0]=-1;
- i=0;
- while(i<lenb)
- {
- while(j!=-1&&b[i]!=b[j])
- j=nex[j];
- nex[++i]=++j;
- }
- }
-
- bool KMP(char a[],char b[],int lena,int lenb)
- {
- int i,j;
- kmp_pre(b,lenb);
- i=j=0;
- while(i<lena)
- {
- while(j!=-1&&a[i]!=b[j])
- j=nex[j];
- i++;j++;
- if(j>=lenb)
- return true;
- }
- return false;
- }
- int n;
- int minlen,minp;
-
- int main()
- {
- while(rd(n)!=EOF&&n)
- {
- minlen=1000;
- for(int i=1;i<=n;i++)
- {
- scanf("%s",str[i]);
- int tplen=strlen(str[i]);
- if(minlen>tplen)
- {
- minlen=tplen;
- minp=i;
- }
- }
- char ans[210];
- ans[0]='\0';
- int maxlen=-1;
- for(int i=0;i<minlen;i++)
- {
- for(int j=i;j<minlen;j++)
- {
- if(j-i+1<maxlen)
- continue;
- char tp[210];
- int cnt=0;
- for(int k=i;k<=j;k++)
- tp[cnt++]=str[minp][k];
- tp[cnt]='\0';
- bool ok=1;
- int lenb=j-i+1;
- for(int k=1;k<=n;k++)
- {
- int lena=strlen(str[k]);
- if(!KMP(str[k],tp,lena,lenb))
- {
- ok=0;
- break;
- }
- }
- if(ok)
- {
- maxlen=strlen(ans);
- if(maxlen<lenb)
- strcpy(ans,tp);
- if(maxlen==lenb)
- {
- if(strcmp(tp,ans)<0)
- strcpy(ans,tp);
- }
- }
- }
- }
- if(maxlen==-1)
- printf("IDENTITY LOST\n");
- else
- {
- for(int i=0;i<maxlen;i++)
- printf("%c",ans[i]);
- cout<<endl;
- }
- }
- return 0;
- }
String Problem
http://acm.hust.edu.cn/vjudge/contest/view.action?cid=70325#problem/O
给定一个字符串比如ababab,编号为1,那么由它可以生成其它五个字符串,每次把头部的字母移到尾部。(循环同构)
ababab 1
bababa 2
ababab 3
bababa 4
ababab 5
bababa 6
求字典序最小的编号以及出现的次数,如果出现多次,输出编号最小的那个,比如字典序最小的为 ababab,一共出现了三次,编号分别为1 3 5,那么要求输出1 (最小编号)以及3(出现次数)。 还有字典序最大的编号以及出现的次数。
编号好求,用最小表示法,返回的就是最小表示法第一个字母的位置,次数就是循环节数。
字符串的最小最大表示法
http://blog.csdn.net/acm_cxlove/article/details/7909087
http://blog.csdn.net/cclsoft/article/details/5467743(这个很容易懂)
http://www.tuicool.com/articles/bmERbm
- #define rd(x) scanf("%d",&x)
- #define rd2(x,y) scanf("%d%d",&x,&y)
- #define rd3(x,y,z) scanf("%d%d%d",&x,&y,&z)
- using namespace std;
- typedef long long ll;
- const int maxn=1000010;
- char b[maxn];
- int nex[maxn];
- int lenb;
-
- void kmp_pre()
- {
- int i,j;
- j=nex[0]=-1;
- i=0;
- while(i<lenb)
- {
- while(-1!=j&&b[i]!=b[j])
- j=nex[j];
- nex[++i]=++j;
- }
- }
-
- int min_max_exp(char b[],int lenb,bool flag)
- {
- int i=0,j=1,k=0;
- while(i<lenb&&j<lenb&&k<lenb)
- {
- int t=b[(j+k)%lenb]-b[(i+k)%lenb];
- if(t==0)
- k++;
- else
- {
- if(flag)
- {
- if(t>0) j+=k+1;
- else i+=k+1;
- }
- else
- {
- if(t>0) i+=k+1;
- else j+=k+1;
- }
- if(i==j) j++;
- k=0;
- }
- }
- return min(i,j);
- }
-
-
- int main()
- {
- while(gets(b))
- {
- lenb=strlen(b);
- kmp_pre();
- int minp=min_max_exp(b,lenb,1);
- int maxp=min_max_exp(b,lenb,0);
- int cir=lenb-nex[lenb];
- int ans=lenb%cir?1:lenb/cir;
- printf("%d %d %d %d\n",minp+1,ans,maxp+1,ans);
- }
- return 0;
- }
How many
http://acm.hust.edu.cn/vjudge/contest/view.action?cid=70325#problem/P
给出n条长度相等的项链,用0 1表示,一条项链如果通过旋转和另外一条一样,
那么这两条就是同一种类。
问给出的这n条项链中有多少种类。
比如
4
0110
1100
1001
0011
0110 1100 1001 都可以旋转得到 0011
旋转也就是每次把第一个字母移到末尾就行了。
对n个字符串变换成最小表示法,然后就找有多少个不同的字符串就可以了。
- #define rd(x) scanf("%d",&x)
- #define rd2(x,y) scanf("%d%d",&x,&y)
- #define rd3(x,y,z) scanf("%d%d%d",&x,&y,&z)
- using namespace std;
- typedef long long ll;
- const int maxn=10010;
-
- int min_max_exp(char b[],int lenb,bool flag)
- {
- int i=0,j=1,k=0;
- while(i<lenb&&j<lenb&&k<lenb)
- {
- int t=b[(j+k)%lenb]-b[(i+k)%lenb];
- if(t==0)
- k++;
- else
- {
- if(flag)
- {
- if(t>0) j+=k+1;
- else i+=k+1;
- }
- else
- {
- if(t>0) i+=k+1;
- else j+=k+1;
- }
- if(i==j) j++;
- k=0;
- }
- }
- return min(i,j);
- }
- int n;
- map<string,int>mp;
- char str[maxn][110];
-
- int main()
- {
- while(rd(n)!=EOF)
- {
- mp.clear();
- for(int i=1;i<=n;i++)
- {
- scanf("%s",str[i]);
- int lenb=strlen(str[i]);
- int p=min_max_exp(str[i],lenb,1);
- string tp="";
-
- for(int k=0;k<lenb;k++)
- {
- int id=(p+k)%lenb;
- tp.push_back(str[i][id]);
- }
-
- mp[tp]++;
- }
- printf("%d\n",mp.size());
- }
- return 0;
- }
Best Reward
http://acm.hust.edu.cn/vjudge/contest/view.action?cid=70325#problem/S
给定26个小写字母的价值,再给定一个由26个字母组成的字符串。
把该字符串切成两部分,如果该部分是回文串,那么该串的价值为每个字母的价值和,
否则该串的价值是0,问切成的这两部分的最大价值是多少。
用扩展KMP,枚举主串的位置,分别判断前i个字母组成的第一部分串是不是回文串,再判断
剩下的一部分串是不是回文串,取最大价值。
主串s,模式串t,extend[]数组是相对于主串的,意思extend[i]意思是主串从第i个位置到末尾组成的
串与模式串整个串的最长公共前缀。
模式串t,长度为lent,next[i]表示 t[i....lent-1]的串与t[0.....lent-1]的最长公共前缀。
主串s,长度为lens,extend[i]表示s[i.....lens-1]的串与t[0.....lent-1]的最长公共前缀。
该题进行两次KMP扩展。
判断主串的前i个字母是不是回文串,用主串作模式串,反转串做主串,进行KMP扩展,就可以判断了.
判断主串剩下的字母是不是回文串,用主串还是做主串,反转串做模式串,进行KMP扩展,就可以判断了。
- #define rd(x) scanf("%d",&x)
- #define rd2(x,y) scanf("%d%d",&x,&y)
- #define rd3(x,y,z) scanf("%d%d%d",&x,&y,&z)
- using namespace std;
- typedef long long ll;
- const int maxn=500010;
- int nex[maxn];
- int extend1[maxn];
- int extend2[maxn];
- char s[maxn];
- char t[maxn];
- int lens,lent;
-
- void pre_kmp(char t[],int lent)
- {
- nex[0]=lent;
- int j=0;
- while(j+1<lent&&t[j]==t[j+1])
- j++;
- nex[1]=j;
- int k=1;
- for(int i=2;i<lent;i++)
- {
- int p=nex[k]+k-1;
- int L=nex[i-k];
- if(i+L<p+1)
- nex[i]=L;
- else
- {
- j=max(0,p-i+1);
- while(i+j<lent&&t[i+j]==t[j])
- j++;
- nex[i]=j;
- k=i;
- }
- }
- }
-
- void EKMP(char s[],char t[],int lens,int lent,int extend[])
- {
- pre_kmp(t,lent);
- int j=0;
- while(j<lens&&j<lent&&t[j]==s[j])
- j++;
- extend[0]=j;
- int k=0;
- for(int i=1;i<lens;i++)
- {
- int p=extend[k]+k-1;
- int L=nex[i-k];
- if(i+L<p+1) extend[i]=L;
- else
- {
- j=max(0,p-i+1);
- while(i+j<lens&&j<lent&&s[i+j]==t[j])
- j++;
- extend[i]=j;
- k=i;
- }
- }
- }
-
- int val[26];
- int sum[maxn];
- int n;
-
- int main()
- {
- rd(n);
- while(n--)
- {
- sum[0]=0;
- for(int i=0;i<26;i++)
- {
- rd(val[i]);
- }
- scanf("%s",s);
- lens=lent=strlen(s);
- for(int i=1;i<=lens;i++)
- {
- sum[i]=sum[i-1]+val[s[i-1]-'a'];
- }
- for(int i=0;i<lens;i++)
- {
- t[i]=s[lens-i-1];
- }
-
-
- EKMP(t,s,lent,lens,extend1);
-
- EKMP(s,t,lens,lent,extend2);
- int ans=0;
- for(int i=1;i<lens;i++)
- {
- int tp=0;
- if(extend1[lens-i]==i)
- tp+=sum[i];
- if(extend2[i]==lens-i)
- tp+=sum[lens]-sum[i];
- if(ans<tp)
- ans=tp;
- }
- printf("%d\n",ans);
- }
- return 0;
- }
Palindrome
http://acm.hust.edu.cn/vjudge/contest/view.action?cid=70325#problem/U
裸的最长回文子串,manacher算法解决
- #define rd(x) scanf("%d",&x)
- #define rd2(x,y) scanf("%d%d",&x,&y)
- #define rd3(x,y,z) scanf("%d%d%d",&x,&y,&z)
- using namespace std;
- typedef long long ll;
- const int maxn=1000010;
- char MA[maxn<<1];
- int MP[maxn<<1];
- void Manacher(char s[],int len)
- {
- int l=0;
- MA[l++]='$';
- MA[l++]='#';
- for(int i=0;i<len;i++)
- {
- MA[l++]=s[i];
- MA[l++]='#';
- }
- MA[l]=0;
- int mx=0,id=0;
- for(int i=0;i<l;i++)
- {
- MP[i]=mx>i?min(MP[2*id-i],mx-i):1;
- while(MA[i+MP[i]]==MA[i-MP[i]])
- MP[i]++;
- if(i+MP[i]>mx)
- {
- mx=i+MP[i];
- id=i;
- }
- }
- }
- char s[maxn];
-
- int main()
- {
- int c=1;
- while(scanf("%s",s)==1)
- {
- if(s[0]=='E')
- break;
- int len=strlen(s);
- Manacher(s,len);
- int ans=0;
- for(int i=0;i<2*len+2;i++)
- ans=max(ans,MP[i]-1);
- printf("Case %d: %d\n",c++,ans);
- }
- return 0;
- }
吉哥系列故事――完美队形II
http://acm.hust.edu.cn/vjudge/contest/view.action?cid=70325#problem/V
要求最长的回文串,条件为左边的数到中间不递减,中间的数到右边不递增。manacher算法
- #define rd(x) scanf("%d",&x)
- #define rd2(x,y) scanf("%d%d",&x,&y)
- #define rd3(x,y,z) scanf("%d%d%d",&x,&y,&z)
- using namespace std;
- typedef long long ll;
- const int maxn=100010;
- int ma[maxn<<1];
- int mp[maxn<<1];
-
- void manacher(int s[],int len)
- {
- int l=0;
- ma[l++]=0;
- ma[l++]=-1;
- for(int i=0;i<len;i++)
- {
- ma[l++]=s[i];
- ma[l++]=-1;
- }
- ma[l]=-1;
- int mx=0,id=0;
- for(int i=0;i<l;i++)
- {
- mp[i]=mx>i?min(mp[2*id-i],mx-i):1;
- while(ma[i+mp[i]]==ma[i-mp[i]]&&ma[i-mp[i]]<=ma[i-mp[i]+2])
- mp[i]++;
- if(i+mp[i]>mx)
- {
- mx=i+mp[i];
- id=i;
- }
- }
- }
- int n;
- int s[maxn];
- int main()
- {
- int t;rd(t);
- while(t--)
- {
- rd(n);
- for(int i=0;i<n;i++)
- rd(s[i]);
- manacher(s,n);
- int ans=0;
- for(int i=0;i<2*n+2;i++)
- ans=max(ans,mp[i]-1);
- printf("%d\n",ans);
- }
- return 0;
- }
Girls' research
http://acm.hust.edu.cn/vjudge/contest/view.action?cid=70325#problem/W
首先给定一个字母ch,那么这个字母就是真正的a, 如果ch=b, 那么 b=a, c=b,d=c..........z=y, a=z.... 如果 ch=c,那么 c=a,d=b,e=c,..... ,z=x,a=y,b=z,也就是一个变换规则。
然后给定一个字符串,求该字符串的最长回文子串,输出该回文子串在原字符串中的左边界和右边界,以及按照变换规则把这部分回文子串输出。
比如样例 b babd (注意格式,中间有一个空格) , 变换规则, b->a c->b,d->c.....a->z
babd的最长回文子串是bab,左边界位置为0,右边界位置为2,按照变换规则输出该回文串为aza.
如果最长回文子串的长度为1,输出No solution!
对原串用Manacher算法求最长回文子串,并记录位置。注意输入格式
- #define rd(x) scanf("%d",&x)
- #define rd2(x,y) scanf("%d%d",&x,&y)
- #define rd3(x,y,z) scanf("%d%d%d",&x,&y,&z)
- using namespace std;
- typedef long long ll;
- const int maxn=200010;
- char ma[maxn<<1];
- int mp[maxn<<1];
- char str[maxn<<1];
- map<int,char>change;
- int ans;
- int p;
-
- void manacher(int len)
- {
- ans=-1;
- int l=0;
- ma[l++]='$';
- ma[l++]='#';
- for(int i=0;i<len;i++)
- {
- ma[l++]=str[i];
- ma[l++]='#';
- }
- ma[l]=0;
- int mx=0,id=0;
- for(int i=0;i<l;i++)
- {
- mp[i]=mx>i?min(mp[2*id-i],mx-i):1;
- while(ma[i+mp[i]]==ma[i-mp[i]])
- mp[i]++;
- if(i+mp[i]>mx)
- {
- mx=i+mp[i];
- id=i;
- }
- if(ans<mp[i]-1)
- {
- ans=mp[i]-1;
- p=i;
- }
- }
- }
-
-
- int main()
- {
- char c;
- while(scanf("%c %s",&c,str)!=EOF)
- {
- getchar();
- int cha=c-'a';
- for(int i=0;i<26;i++)
- change[i]=((i+26-cha)%26+'a');
-
-
-
- int len=strlen(str);
- manacher(len);
- if(ans<2)
- {
- printf("No solution!\n");
- continue;
- }
- int L=p-ans+1;
- int l=L/2-1;
- int r=l+ans-1;
- printf("%d %d\n",l,r);
- for(int k=l;k<=r;k++)
- printf("%c",change[str[k]-'a']);
- printf("\n");
- }
- return 0;
- }
最长回文
http://acm.hust.edu.cn/vjudge/contest/view.action?cid=70325#problem/X
学习manacher算法,复杂度O(n)
资料:http://blog.csdn.net/dyx404514/article/details/42061017
- #define rd(x) scanf("%d",&x)
- #define rd2(x,y) scanf("%d%d",&x,&y)
- #define rd3(x,y,z) scanf("%d%d%d",&x,&y,&z)
- using namespace std;
- typedef long long ll;
- const int maxn=110010;
- char MA[maxn<<1];
- int MP[maxn<<1];
- void Manacher(char s[],int len)
- {
- int l=0;
- MA[l++]='$';
- MA[l++]='#';
- for(int i=0;i<len;i++)
- {
- MA[l++]=s[i];
- MA[l++]='#';
- }
- MA[l]=0;
- int mx=0,id=0;
- for(int i=0;i<l;i++)
- {
- MP[i]=mx>i?min(MP[2*id-i],mx-i):1;
- while(MA[i+MP[i]]==MA[i-MP[i]])
- MP[i]++;
- if(i+MP[i]>mx)
- {
- mx=i+MP[i];
- id=i;
- }
- }
- }
- char s[maxn];
-
- int main()
- {
- while(scanf("%s",s)==1)
- {
- int len=strlen(s);
- Manacher(s,len);
- int ans=0;
- for(int i=0;i<2*len+2;i++)
- ans=max(ans,MP[i]-1);
- printf("%d\n",ans);
- }
- return 0;
- }
Wow! Such Doge!
http://acm.hust.edu.cn/vjudge/contest/view.action?cid=70325#problem/Y
求给定的文本中有多少个"doge",大小写不区分,先把所有大写字母转为小写字母,然后扫描一遍就可以了。注意输入格式。
输入:
adoge
cutedo
yourge
blownDoge
lovelyDooge
Wow! Such Dooooooooooooooge!!!
D0ge
dOge DOGE
dogedoge
输出:
6
- #define rd(x) scanf("%d",&x)
- #define rd2(x,y) scanf("%d%d",&x,&y)
- #define rd3(x,y,z) scanf("%d%d%d",&x,&y,&z)
- using namespace std;
- typedef long long ll;
- const int maxn=10010;
- char str[100010];
-
- int main()
- {
- int cnt=0;
- while(gets(str))
- {
- int len=strlen(str);
- for(int i=0;i<len;i++)
- if(isalpha(str[i]))
- str[i]=tolower(str[i]);
- for(int i=0;i<len;i++)
- {
-
- if(i+3<len)
- {
- if(str[i]=='d'&&str[i+1]=='o'&&str[i+2]=='g'&&str[i+3]=='e')
- {
- cnt++;
-
- i+=3;
-
- }
- }
- }
- }
- printf("%d\n",cnt);
- return 0;
- }