https://vjudge.net/problem/26968/origin
题意:给一个串,问最少添加一个任意字符可以使其变成一个回文串
思路是拓展KMP,将串反过来匹配,利用extend数组,第一个可以拓展到有重叠部分的下标之前的就是要添加的
#include<stdio.h>
#include<iostream>
#include<string.h>
#include<algorithm>
using namespace std;
const int maxn=1e6+100;
int nextt[maxn],extend[maxn];
char s[maxn],p[maxn];
void pre_EKMP(char x[],int m,int next[])
{
next[0]=m;
int j=0;
while(j+1<m&&x[j]==x[j+1])j++;
next[1]=j;
int k=1;
for(int i=2;i<m;i++)
{
int p=next[k]+k-1;
int L=next[i-k];
if(i+L<p+1)
next[i]=L;
else
{
j=max(0,p-i+1);
while(i+j<m&&x[i+j]==x[j])j++;
next[i]=j;
k=i;
}
}
}
void EKMP(char x[],int m,char y[],int n,int next[],int extend[])
{
pre_EKMP(x,m,next);
int j=0;
while(j<n&&j<m&&x[j]==y[j])j++;
extend[0]=j;
int k=0;
for(int i=1;i<n;i++)
{
int p=extend[k]+k-1;
int L=next[i-k];
if(i+L<p+1)
extend[i]=L;
else
{
j=max(0,p-i+1);
while(i+j<n&&j<m&&y[i+j]==x[j])j++;
extend[i]=j;
k=i;
}
}
}
int main()
{
int T,i,tca=1;
cin>>T;
while(T--)
{
scanf("%s",s);
int len1=strlen(s);
strcpy(p,s);
reverse(p,p+len1);
EKMP(p,len1,s,len1,nextt,extend);
int ans=0;
for(i=0;i<len1;i++)
{
if(extend[i]*2>len1-i)
{
ans=len1+i;
break;
}
}
printf("Case %d: %d\n",tca++,ans);
}
return 0;
}
看了看别人的题解,似乎并没有那么麻烦,
也是把串反过来匹配,直接KMP就可以,反过来的串做匹配串,原串主串。主串的最后一个元素,其实也就是后缀,和匹配串的前缀的最长公共部分,也就是KMP到最后一个主串元素(i==n)时对应的匹配串下标(j)
答案就是2*n-j
相当于减掉了重叠的部分,已经构成回文的部分,
#include<stdio.h>
#include<iostream>
#include<string.h>
#include<algorithm>
using namespace std;
const int maxn=1e6+100;
int nextt[maxn];
char s[maxn],p[maxn];
void getnext(int m,char *str)
{
int j,k;
j=0,k=-1,nextt[0]=-1;
while(j<m)
if(k==-1||str[j]==str[k])
nextt[++j]=++k;
else
k=nextt[k];
}
int KMP(int n,char *s,int m,char *p)
{
getnext(m,p);
int i=0,j=0;
while(i<n&&j<m)
{
if(j==-1||s[i]==p[j])
i++,j++;
else
j=nextt[j];
}
return j;
}
int main()
{
int T,i,tca=1;
cin>>T;
while(T--)
{
scanf("%s",s);
int len1=strlen(s);
strcpy(p,s);
reverse(p,p+len1);
printf("Case %d: %d\n",tca++,2*len1-KMP(len1,s,len1,p));
}
return 0;
}