题目链接:http://poj.org/status?problem_id=3376
题意给出一连串多组字符串,问这些字符串两两相连能形成多少个回文串。
参考别人的思路,看了下扩展kmp,才明白怎么解决这一题~
如果两个字符串的公共前缀为其中某一个的整,并且另一个串的余下部分为回文串,则一个字符串与另一个字符串的逆序串边接可成一回文串。
所以这题中,将字符串分原串和逆串进行处理,在字典树中找公共前缀, 而非公共前缀部分的回文串判断,用扩展kmp解决。
Code:
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define N 2000012
char s[N];
int len,n;
int f[N],t[N];
typedef struct talT{
int c,leaf;
talT *nxt[27];
}Trie;
Trie Memory[N*3];
Trie *r;
int size;
bool bis[N];
int nxt[N],ext[N];
Trie *NewTrie()
{
return &Memory[size++];
}
void Insert(int ix)
{
int i,a;
Trie *cur=r;
for(i=f[ix];i<t[ix];i++){
cur->c+=bis[i];
a=s[i]&31;
if(!cur->nxt[a])
cur->nxt[a]=NewTrie();
cur=cur->nxt[a];
}
cur->leaf++;
}
void ExtendKMP(char s[],char t[],int l,int ix)
{
int i,j,k;
int Len,L;
//next
j=0;
while(t[j]==t[1+j]&&j+1<l) j++;
nxt[1]=j,k=1;
for(i=2;i<l;i++){
Len=k+nxt[k],L=nxt[i-k];
if(Len>L+i) nxt[i]=L;
else{
j=Len-i>0?Len-i:0;
while(t[i+j]==t[j]&&i+j<l) j++;
nxt[i]=j,k=i;
}
}
//extend
j=0;
while(s[j]==t[j]&&j<l) j++;
ext[0]=j,k=0;
for(i=1;i<l;i++){
Len=k+ext[k],L=nxt[i-k];
if(Len>L+i) ext[i]=L;
else{
j=Len-i>0?Len-i:0;
while(s[i+j]==t[j]&&i+j<l) j++;
ext[i]=j,k=i;
}
}
for(i=0;i<l;i++){
if(ext[i]==l-i)
bis[ix+i]=1;
}
}
__int64 Query()
{
int i,j;
__int64 ret;
int a;
Trie *cur;
for(ret=i=0;i<n;i++){
cur=r;
for(j=t[i];j<f[i+1];j++){
a=s[j]&31;
cur=cur->nxt[a];
if(!cur) break;
if(bis[j+1]||j+1==f[i+1])
ret+=cur->leaf;
}
if(cur) ret+=cur->c;
}
return ret;
}
int main()
{
int i,j,l;
scanf("%d",&n);
len=0;size=0;
r=NewTrie();
for(i=0;i<n;i++){
scanf("%d%s",&l,s+len);
for(j=0;j<l;j++)
s[len+(l<<1)-j-1]=s[len+j];
ExtendKMP(s+len,s+len+l,l,len);
ExtendKMP(s+len+l,s+len,l,len+l);
f[i]=len;
t[i]=len+l;
len+=l<<1;
Insert(i);
}
f[i]=len;
printf("%I64d\n",Query());
return 0;
}