题意:求每个本质不同的回文串的所有回文子串的总数
思路:我们统计一个串的回文子串的个数,可以将这些子串分为两类,一类是父亲节点的所有子串(包含父亲节点),另一种是所有可能的回文后缀。注意着两者有交集,所以需要标记是否访问过。这两种包含所有的情况,原因是一个字符串的所有回文子串可以分为两类,一类是包含最后一个字符的,另一类是不包含最后一个字符的。
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 300005;
char str[maxn];
struct PAM{
int last, //当前最长回文后缀的节点标号
n, //字符串的长度
SZ; //节点的个数
int ch[maxn][26], // 儿子指针
fail[maxn], // 指向长度小于当前节点的最长回文后缀
cnt[maxn], //节点所代表的回文串作为最长回文后缀出现的次数 或 长度小于该串的回文子串的个数
len[maxn], //节点所代表的回文后缀的长度
s[maxn], //字符串
vis[maxn];
void init(int _n) {
SZ=1,len[1]=-1,s[0]=-1,fail[0]=1;last = 1;n = 0;
for( int i =0;i <= _n+1;i++ ){
memset( ch[i],0,sizeof(ch[i]) );
cnt[i] = 0;
}
}
int getfail(int x) {
while(s[n-len[x]-1]!=s[n]) x=fail[x];
return x;
}
void add(int t) {
s[++n]=t;int cur=getfail(last);
if(!ch[cur][t]) {
fail[++SZ]=ch[getfail(fail[cur])][t];
ch[cur][t]=SZ,len[SZ]=len[cur]+2;
}
last=ch[cur][t];
}
void dfs( int x,int fa ){
if( fa >= 2 ){
cnt[x] += cnt[fa];
cnt[x]++;
}
vis[fa] = 1;
stack<int> st;
int p = fail[x];
while( p >1 && !vis[p] ){
cnt[x]++;
vis[p] = 1;
st.push( p );
p = fail[p];
}
for( int i = 0;i < 26;i++ ){
if( !ch[x][i] ) continue;
dfs(ch[x][i],x);
}
vis[fa] = 0;
while( st.size() ){
vis[ st.top() ] = 0;
st.pop();
}
}
LL count() {
LL re = 0;
dfs( 0,0 );
dfs(1,0);
for( int i = 2;i <= SZ;i++ ){
re += cnt[i];
}
return re;
}
}g;
int main(){
int T;
scanf("%d",&T);
for(int tt = 1;tt <= T;tt++) {
printf("Case #%d: ",tt);
scanf("%s", str);
int len = strlen(str);
g.init(len);
for (int i = 0; i < len; i++) {
g.add(str[i] - 'a');
}
cout << g.count() << endl;
}
}