http://vjudge.net/contest/70325#problem/T
kuangbin专题十六字最后一发
题意:n个字符串,每个字符串长度为l,将字符串两两自由组合一共有(n * n种方案)判断其中组合之后为回文串的字符有多少个.
做法:首先由两个字符串a,b.要使它们能组成回文串有三种情况
1.alen < blen 时
a是b的反串前缀,且b的剩余部分可以认为是后缀是回文串
2.alen > blen
b的反串是a的前缀,且a的后缀是回文串
3.alen == blen
b是a的反串
现将所有字符串建立在字典树中。然后求s和s的反串的后缀中是回文串的位置,也就是说从哪个位置开始一直到最后是回文串实现就是(i + extend[i] == n,以正串为例,反串的首位就是正串的最后一位相等 )。用字典树中的参数huiwen记录从该字母开始回文串的个数,注意回文记录的只是以正串为后缀的个数
另外:因为只给了字符串的总长度,所以,只开一维的字符串数组,每次接着上次字符串结束的位置开始即可。为了方便,记录字符串开始的位置,和结束的位置
#include<stdio.h>
#include<iostream>
#include<string.h>
#include<map>
#include<algorithm>
using namespace std;
const int MAXN=2000007;
long long ans = 0;
bool flag[2][MAXN];
int nexta[MAXN];
int extend[MAXN];
void EKMP(char s[],char t[],int bg,int ed,int sign)//s主串,t模式串
{
nexta[bg] = ed - bg;
int k;
for(k = 0; k + 1 + bg< ed && t[k + bg] == t[k + 1 + bg]; k ++ )
{
}
nexta[bg + 1] = k;
k = bg + 1;
int p,L,j;
for(int i = 2 + bg; i < ed; i ++)
{
p = k + nexta[k] - 1, L = nexta[bg + i - k];
if(i + L <= p)
{
nexta[i] = L;
}
else
{
j = p - i + 1;
if(j < 0)
j = 0;
while(i + j < ed && t[i + j] == t[bg + j])
{
j++;
}
nexta[i] = j;
k = i;
}
}
for(k = 0; k+ bg < ed && s[k+bg] == t[k+bg]; k ++ )
{
}
extend[bg] = k;
k = bg;
for(int i = bg + 1; i < ed; i ++)
{
p = k + extend[k] - 1, L = nexta[bg + i - k];//注意p是加extend
if(i + L <= p)
{
extend[i] = L;
}
else
{
int j = p - i + 1;
if(j < 0)
j = 0;
while(i + j < ed &&j < ed && s[i + j] == t[bg + j])
{
j++;
}
extend[i] = j;
k = i;
}
}
for(int i = bg; i < ed; i ++)
{
if(extend[i] + i == ed)
{
flag[sign][i] = true;
}
}
}
struct node
{
int nexta[26];
int countx;
int huiwen;
} f[2000006];
void init(int a)//新增函数
{
memset(f[a].nexta,-1,sizeof(f[a].nexta));
f[a].countx = 0;
f[a].huiwen = 0;
}
int cnt = 1;
void build(char a[],int bg,int ed)
{
int p = 0;
int t;
for(int i = bg; i < ed; i++)
{
t = a[i] - 'a';
if(flag[0][i] == true)
{
f[p].huiwen ++;
}
if(f[p].nexta[t] == -1)
{
f[p].nexta[t] = cnt;
init(cnt);
cnt++;
}
p = f[p].nexta[t];
}
f[p].countx ++;//单词相同的个数。hud1251则是前缀相同的个数
}
void findx(char a[],int bg,int ed)
{
int k = 0;
int t;
for(int i = bg; i < ed; i ++)
{
t = a[i] - 'a';
k = f[k].nexta[t];
if(k == -1)
break;
if((i + 1< ed && flag[1][i + 1] == true)||i + 1 == ed)//如果两个字符串是完全相等的则满足i + 1 == ed的条件,又因为以k结尾的字符有两个,一下子加上了两个。且此时k != -1
{
ans += f[k].countx;//表示以k结束的字符串的个数
}
}
if(k != -1)//反串比正串短的情况,要加上正串后面都是回文串的情况
{
ans += f[k].huiwen;
}
//注意因为是要匹配n * n次,保证不重不漏。
return;
}
char s[MAXN],ins[MAXN];
int bg[MAXN],ed[MAXN];
int main()
{
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
memset(nexta,0,sizeof(nexta));
memset(extend,0,sizeof(extend));
init(0);
memset(flag,false,sizeof(flag));
int T,n;
int L = 0;
scanf("%d",&T);
for(int t = 0; t < T; t++)
{
scanf("%d%s",&n,s + L);
for(int i = 0; i < n; i++)
{
ins[i + L] = s[L + n - 1 - i];
}
bg[t] = L;
ed[t] = L + n;
L += n;
EKMP(s,ins,bg[t],ed[t],0);
EKMP(ins,s,bg[t],ed[t],1);
build(s,bg[t],ed[t]);
}
for(int i = 0; i < T; i ++)
{
findx(ins,bg[i],ed[i]);
}
cout<<ans<<endl;
return 0;
}