回文自动机(回文树)
回文树的性质和构建
功能
求串S前缀0~i内本质不同回文串的个数
求串S内每一个本质不同回文串出现的次数
求串S内回文串的个数(其实就是1和2结合起来)
求以下标i结尾的回文串的个数
和AC自动机相似
回文树每个节点都代表一个回文子串,其fail指向最长回文后缀。并且该后缀也是一个border
要知道一个节点(一个位置)结尾的所有回文串,只需要跳完fail
但是统计一个点开始的,则需要把串倒过来建回文树
一个节点代表的回文串出现次数:fail树size之和。
从根到一个节点的路径,是该节点代表回文串的一半
模板
/*
最小回文划分
此代码求[1..i]的最小偶数个回文和奇数个回文串的划分。注意拆分成k个则一定可以拆分成k + 2个(两头独立)
利用slink链长logn的性质,可以用来求诸如回文划分方案数等东西
*/
namespace pam{
const int minchar = 'a';
const int maxchar = 26;
const int N = 3e5 + 10;
int tot,last,len[N],fail[N],nxt[N][maxchar],sz[N],quick[N][maxchar],pnt[N],half[N];
int f[N],trim[N],pos[N],diff[N],slink[N];
int ans[N][2],s_ans[N][2];
void init(){
rep(i,0,tot) len[i] = fail[i] = sz[i] = half[i] = pnt[i] = f[i] = trim[i] = 0 , memset(nxt[i],0,sizeof nxt[i]);
ch[0]='*',tot=1,len[1]=-1,fail[0]=1;
rep(i,0,25) quick[0][i] = 1;
last = 0;
}
void ins(int c,int id){
if ( ch[id] != ch[id - len[last] - 1] ) last = quick[last][c];
//while(ch[id]!=ch[id-len[last]-1]) last=fail[last];
if(nxt[last][c]) last=nxt[last][c];
else{
//int k=fail[last];
//while(ch[id]!=ch[id-len[k]-1]) k=fail[k];
//k=nxt[k][c];
int k = nxt[quick[last][c]][c];
int v = ++tot;
fail[v]=k,len[v]=len[last]+2;
memcpy(quick[v],quick[k],sizeof quick[k]);//字符集很大,可持久化quick数组
quick[v][ch[id - len[k]] - minchar] = k;
diff[v] = len[v] - len[fail[v]];
if ( diff[v] == diff[fail[v]] ) slink[v] = slink[fail[v]];
else slink[v] = fail[v];
/*
求长度小于等于len[v] / 2的最长回文后缀,复杂度均摊正确
if ( len[v] == 1 ) half[v] = 0;
else{
int p = half[last];
while ( ch[id] != ch[id - len[p] - 1] || (len[p] + 2 > len[v] / 2) ) p = fail[p];
half[v] = nxt[p][c];
}*/
nxt[last][c]=v;
pnt[v] = last;
last = v;
}
sz[last]++;
pos[id] = last;
}
void build(){
init();
rep(i,1,n){
ins(ch[i] - minchar,i);
}
// rep(i,1,n) cout<<i<<" : "<<pos[i]<<" "<<len[pos[i]]<<endl;
}
void solve(){
ans[0][0] = 0;
ans[0][1] = inf;
rep(i,1,n) ans[i][0] = ans[i][1] = inf;
rep(i,1,n){
//cout<<"cur : "<<i<<endl;
for (int x = pos[i] ; len[x] > 0 ; x = slink[x]){
// cout<<x<<" "<<slink[x]<<" "<<len[slink[x]]<<" "<<len[x]<<" "<<diff[x]<<endl;
s_ans[x][0] = ans[i - len[slink[x]] - diff[x]][0];
s_ans[x][1] = ans[i - len[slink[x]] - diff[x]][1];
if ( diff[x] == diff[fail[x]] ){
s_ans[x][0] = min(s_ans[fail[x]][0],s_ans[x][0]);
s_ans[x][1] = min(s_ans[fail[x]][1],s_ans[x][1]);
}
ans[i][0] = min(ans[i][0],s_ans[x][1] + 1);
ans[i][1] = min(ans[i][1],s_ans[x][0] + 1);
}
if ( ans[i][1] > n ) printf("-1 ");
else printf("%d ",ans[i][1]);
if ( ans[i][0] > n ) printf("-2\n");
else printf("%d\n",ans[i][0]);
}
}
}
using namespace pam