题意:
给定n个字符串,这些字符串可以进行拼接(前后拼接共n*n种拼接情况),求拼接后为回文串的情况数。
思路:
先举个两串拼接的例子:A串(aaabaa)B串(baaa)A串拼B串(aaabaabaaa)B串拼A串(baaaaaabaa)明显可以看出A串拼B串是一个回文串而B串拼A串不是一个回文串。
可以发现若两串相拼为回文串,则后半部分字符串的反串的前缀是和前半部分字符串的前缀是完全相同的且相对较长那一串的剩余部分是一个回文串(举个后半部分较长的例子:A串(aab)B串(bbbaa))。
抓住两个关键点:一。字符串前缀完全相同,二。相对较长的剩余部分为回文串。
一。字符串前缀完全相同:前缀匹配是trie树的强项,所以我们使用tire树维护每一个串。
二。剩余后缀为回文串:以原串为母串反串为子串进行扩展KMP就可以得出各后缀是否为回文串。
通过tire树维护正序串(做为前半部分),以反序串作为询问查询答案(做为后半部分),需要注意两种情况。
一。反序查询串短:则我们需要知道有多少正序串,去掉当前前缀剩余部分为回文串(通过cnt[]维护)。
二。反序查询串长:则我们需要知道有多少正序串,到此点结束了且反序串剩余部分为回文串(通过val[]维护)。
C++代码:
#include<map>
#include<set>
#include<stack>
#include<cmath>
#include<queue>
#include<vector>
#include<string>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn = 2000010;
char S[maxn],T[maxn];
int Next[maxn],Ex[maxn];
void GetNext( char *s , int *Next )
{
int c = 0,len = strlen(s);
Next[0] = len;
while ( s[c]==s[c+1]&&c+1<len ) c++;
Next[1] = c;
int po = 1;
for ( int i=2 ; i<len ; i++ )
{
if ( Next[i-po]+i<Next[po]+po )
Next[i] = Next[i-po];
else
{
int t = Next[po]+po-i;
if ( t<0 ) t = 0;
while ( i+t<len&&s[t]==s[i+t] ) t++;
Next[i] = t;
po = i;
}
}
}
void ExKmp( char *s1 , char *s2 , int *Next , int *Ex )
{
int c = 0,len = strlen(s1),l2 = strlen(s2);
GetNext( s2 , Next );
while ( s1[c]==s2[c]&&c<len&&c<l2 ) c++;
Ex[0] = c;
int po = 0;
for ( int i=1 ; i<len ; i++ )
{
if ( Next[i-po]+i<Ex[po]+po )
Ex[i] = Next[i-po];
else
{
int t = Ex[po]+po-i;
if ( t<0 ) t = 0;
while( i+t<len&&t<l2&&s1[i+t]==s2[t] ) t++;
Ex[i] = t;
po = i;
}
}
}
int tot,tree[maxn][26],cnt[maxn],val[maxn];
int NewNode()
{
for ( int i=0 ; i<26 ; i++ )
tree[tot][i] = 0;
cnt[tot] = 0;
val[tot] = 0;
return tot++;
}
void Insert( char *s )
{
int len = strlen(s),now = 0;
for ( int i=0 ; i<len ; i++ )
{
int tmp = s[i]-'a';
if ( !tree[now][tmp] )
tree[now][tmp] = NewNode();
now = tree[now][tmp];
if ( i+1!=len&&Ex[i+1]+i+1==len )
cnt[now]++;
}
cnt[now]++;
val[now]++;
}
int Query( char *s )
{
int len = strlen(s),now = 0,res = 0;
for ( int i=0 ; i<len ; i++ )
{
int tmp = s[i]-'a';
if ( !tree[now][tmp] )
return res;
now = tree[now][tmp];
if ( i+1!=len&&Ex[i+1]+i+1==len )
res += val[now];
}
return res+cnt[now];
}
char Str[maxn];
bool Div[maxn];
int main()
{
for ( int n ; scanf ( "%d" , &n )==1 ; )
{
tot = 0;
NewNode();
int pos = 0,len;
long long ans = 0;
memset ( Div , false , sizeof(Div) );
for ( int i=1 ; i<=n ; i++ )
{
scanf ( "%d%s" , &len , S );
for ( int j=0 ; j<len ; j++ )
T[j] = S[len-j-1],Str[pos++] = S[len-j-1];
T[len] = '\0';
Div[pos] = true;
ExKmp( S , T , Next , Ex );
Insert( S );
}
for ( int i=0,l=0 ; i<pos ; i++ )
{
if ( Div[i+1] )
{
S[l++] = Str[i];
S[l] = '\0';
for ( int j=0 ; j<l ; j++ )
T[j] = S[l-j-1];
T[l] = '\0';
ExKmp( S , T , Next , Ex );
ans += (long long)Query( S );
l = 0;
}
else
S[l++] = Str[i];
}
printf ( "%lld\n" , ans );
}
return 0;
}