理论部分参考论文:回文树及其实现 翁文涛
模板
const int MAXN = 100005 ;
const int N = 26 ;
struct Palindromic_Tree {
int next[MAXN][N] ;//next指针,next指针和字典树类似,指向的串为当前串两端加上同一个字符构成
int fail[MAXN] ;//fail指针,失配后跳转到fail指针指向的节点
int cnt[MAXN] ;
int num[MAXN] ;
int len[MAXN] ;//len[i]表示节点i表示的回文串的长度
int S[MAXN] ;//存放添加的字符
int last ;//指向上一个字符所在的节点,方便下一次add
int n ;//字符数组指针
int p ;//节点指针
int newnode ( int l ) {//新建节点
for ( int i = 0 ; i < N ; ++ i ) next[p][i] = 0 ;
cnt[p] = 0 ;
num[p] = 0 ;
len[p] = l ;
return p ++ ;
}
void init () {//初始化
p = 0 ;
newnode ( 0 ) ;
newnode ( -1 ) ;
last = 0 ;
n = 0 ;
S[n] = -1 ;//开头放一个字符集中没有的字符,减少特判
fail[0] = 1 ;
}
int get_fail ( int x ) {//和KMP一样,失配后找一个尽量最长的
while ( S[n - len[x] - 1] != S[n] ) x = fail[x] ;
return x ;
}
void add ( int c ) {
c -= 'a' ;
S[++ n] = c ;
int cur = get_fail ( last ) ;//通过上一个回文串找这个回文串的匹配位置
if ( !next[cur][c] ) {//如果这个回文串没有出现过,说明出现了一个新的本质不同的回文串
int now = newnode ( len[cur] + 2 ) ;//新建节点
fail[now] = next[get_fail ( fail[cur] )][c] ;//和AC自动机一样建立fail指针,以便失配后跳转
next[cur][c] = now ;
num[now] = num[fail[now]] + 1 ;
}
last = next[cur][c] ;
cnt[last] ++ ;
}
void count () {
for ( int i = p - 1 ; i >= 0 ; -- i ) cnt[fail[i]] += cnt[i] ;
//父亲累加儿子的cnt,因为如果fail[v]=u,则u一定是v的子回文串!
}
} ;
Palindromic Tree
【bzoj3676】[Apio2014]回文串
bzoj2565: 最长双回文串
https://www.lydsy.com/JudgeOnline/problem.php?id=2565
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 100005 ;
const int N = 26 ;
struct Palindromic_Tree {
int next[MAXN][N] ;//next指针,next指针和字典树类似,指向的串为当前串两端加上同一个字符构成
int fail[MAXN] ;//fail指针,失配后跳转到fail指针指向的节点
int cnt[MAXN] ;
int num[MAXN] ;
int len[MAXN] ;//len[i]表示节点i表示的回文串的长度
int S[MAXN] ;//存放添加的字符
int last ;//指向上一个字符所在的节点,方便下一次add
int n ;//字符数组指针
int p ;//节点指针
int newnode ( int l ) {//新建节点
for ( int i = 0 ; i < N ; ++ i ) next[p][i] = 0 ;
cnt[p] = 0 ;
num[p] = 0 ;
len[p] = l ;
return p ++ ;
}
void init () {//初始化
p = 0 ;
newnode ( 0 ) ;
newnode ( -1 ) ;
last = 0 ;
n = 0 ;
S[n] = -1 ;//开头放一个字符集中没有的字符,减少特判
fail[0] = 1 ;
}
int get_fail ( int x ) {//和KMP一样,失配后找一个尽量最长的
while ( S[n - len[x] - 1] != S[n] ) x = fail[x] ;
return x ;
}
int add ( int c ) {
c -= 'a' ;
S[++ n] = c ;
int cur = get_fail ( last ) ;//通过上一个回文串找这个回文串的匹配位置
if ( !next[cur][c] ) {//如果这个回文串没有出现过,说明出现了一个新的本质不同的回文串
int now = newnode ( len[cur] + 2 ) ;//新建节点
fail[now] = next[get_fail ( fail[cur] )][c] ;//和AC自动机一样建立fail指针,以便失配后跳转
next[cur][c] = now ;
num[now] = num[fail[now]] + 1 ;
}
last = next[cur][c] ;
cnt[last] ++ ;
return len[last];
}
void count () {
for ( int i = p - 1 ; i >= 0 ; -- i ) cnt[fail[i]] += cnt[i] ;
//父亲累加儿子的cnt,因为如果fail[v]=u,则u一定是v的子回文串!
}
} ;
Palindromic_Tree T;
char s[100050];
int len[100050];
int main(){
scanf("%s",s);
int n=strlen(s);
T.init();
int ans=0;
for(int i=n-1;i>=0;i--){
len[i]=T.add(s[i]);
}
T.init();
for(int i=0;i<n-1;i++){
ans=max(ans,T.add(s[i])+len[i+1]);
}
printf("%d\n",ans);
return 0;
}
bzoj2160: 拉拉队排练
https://www.lydsy.com/JudgeOnline/problem.php?id=2160
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int MAXN = 1000005 ;
const int N = 26 ;
struct Palindromic_Tree {
int next[MAXN][N] ;//next指针,next指针和字典树类似,指向的串为当前串两端加上同一个字符构成
int fail[MAXN] ;//fail指针,失配后跳转到fail指针指向的节点
int cnt[MAXN] ;
int num[MAXN] ;
int len[MAXN] ;//len[i]表示节点i表示的回文串的长度
int S[MAXN] ;//存放添加的字符
int last ;//指向上一个字符所在的节点,方便下一次add
int n ;//字符数组指针
int p ;//节点指针
int newnode ( int l ) {//新建节点
for ( int i = 0 ; i < N ; ++ i ) next[p][i] = 0 ;
cnt[p] = 0 ;
num[p] = 0 ;
len[p] = l ;
return p ++ ;
}
void init () {//初始化
p = 0 ;
newnode ( 0 ) ;
newnode ( -1 ) ;
last = 0 ;
n = 0 ;
S[n] = -1 ;//开头放一个字符集中没有的字符,减少特判
fail[0] = 1 ;
}
int get_fail ( int x ) {//和KMP一样,失配后找一个尽量最长的
while ( S[n - len[x] - 1] != S[n] ) x = fail[x] ;
return x ;
}
int add ( int c ) {
c -= 'a' ;
S[++ n] = c ;
int cur = get_fail ( last ) ;//通过上一个回文串找这个回文串的匹配位置
if ( !next[cur][c] ) {//如果这个回文串没有出现过,说明出现了一个新的本质不同的回文串
int now = newnode ( len[cur] + 2 ) ;//新建节点
fail[now] = next[get_fail ( fail[cur] )][c] ;//和AC自动机一样建立fail指针,以便失配后跳转
next[cur][c] = now ;
num[now] = num[fail[now]] + 1 ;
}
last = next[cur][c] ;
cnt[last] ++ ;
return len[last];
}
void count () {
for ( int i = p - 1 ; i >= 0 ; -- i ) cnt[fail[i]] += cnt[i] ;
//父亲累加儿子的cnt,因为如果fail[v]=u,则u一定是v的子回文串!
}
} ;
Palindromic_Tree T;
char s[MAXN];
ll len[MAXN];
ll n,k;
bool cmp(int x,int y){
return x>y;
}
const ll mod=19930726;
ll fpow(ll a, ll b)
{
ll ans = 1;//A
a = a % mod;
while (b > 0)
{
if (b&1)
ans = ans * a % mod;
b >>= 1;
a = a * a % mod;
}
return ans;
}
int main(){
scanf("%lld%lld",&n,&k);
scanf("%s",s);
T.init();
ll ans=1;
ll m=0;
for(int i=0;i<n;i++){
T.add(s[i]);
}
T.count();
int mx=0;
for(int i=2;i<=n+2;i++){
if(T.len[i]&1){
len[T.len[i]]+=T.cnt[i];
mx=max(mx,T.len[i]);
m+=T.cnt[i];
}
}
if(k>m){
printf("-1\n");
return 0;
}
for(int i=mx;i>=1;i-=2){
ll mn=min(k,len[i]);
k-=mn;
if(mn) ans=(ans*fpow(i,mn))%mod;
}
printf("%lld\n",ans);
return 0;
}
Codeforces 17E. Palisection
https://codeforces.com/problemset/problem/17/E
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 2e6+5;
const int mod = 51123987;
int n,fa[N],len[N],dep[N],tot,last,p1[N],p2[N],ans;
int to[N],nxt[N],ww[N],head[N],cnt;
char s[N];
void init()
{
fa[last=0]=fa[1]=1;
len[tot=1]=-1;
memset(head,0,sizeof(head));cnt=0;
}
void link(int u,int v,int c)
{
to[++cnt]=v;nxt[cnt]=head[u];ww[cnt]=c;
head[u]=cnt;
}
int tr(int v,int c)
{
for (int e=head[v];e;e=nxt[e])
if (ww[e]==c) return to[e];
return 0;
}
void extend(int c,int n)
{
int v=last;
while (s[n-len[v]-1]!=s[n]) v=fa[v];
if (!tr(v,c))
{
int u=++tot,k=fa[v];
len[u]=len[v]+2;
while (s[n-len[k]-1]!=s[n]) k=fa[k];
fa[u]=tr(k,c);dep[u]=dep[fa[u]]+1;
link(v,u,c);
}
last=tr(v,c);
}
int main()
{
scanf("%d",&n);
scanf("%s",s+1);
init();
for (int i=1;i<=n;++i) extend(s[i]-'a',i),(ans+=(p1[i]=dep[last]))%=mod;
ans=1ll*ans*(ans-1)/2%mod;
reverse(s+1,s+n+1);
init();
for (int i=1;i<=n;++i) extend(s[i]-'a',i),p2[n-i+1]=dep[last];
for (int i=n;i;--i) (p2[i]+=p2[i+1])%=mod;
for (int i=1;i<=n;++i) ans=(ans-1ll*p1[i]*p2[i+1]%mod+mod)%mod;
printf("%d\n",ans);
return 0;
}
Palindrome Mouse
https://ac.nowcoder.com/acm/contest/886/C
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int MAXN = 100005 ;
const int N = 26 ;
struct Palindromic_Tree {
int next[MAXN][N] ;//next指针,next指针和字典树类似,指向的串为当前串两端加上同一个字符构成
int fail[MAXN] ;//fail指针,失配后跳转到fail指针指向的节点
int cnt[MAXN] ;
int num[MAXN] ;//右端点在这一位子串的个数
int len[MAXN] ;//len[i]表示节点i表示的回文串的长度
int S[MAXN] ;//存放添加的字符
int last ;//指向上一个字符所在的节点,方便下一次add
int n ;//字符数组指针
int p ;//节点指针
int sz[MAXN],c[MAXN],vis[MAXN];
int newnode ( int l ) {//新建节点
for ( int i = 0 ; i < N ; ++ i ) next[p][i] = 0 ;
cnt[p] = 0 ;
num[p] = 0 ;
len[p] = l ;
return p ++ ;
}
void init () {//初始化
p = 0 ;
newnode ( 0 ) ;
newnode ( -1 ) ;
last = 0 ;
n = 0 ;
S[n] = -1 ;//开头放一个字符集中没有的字符,减少特判
fail[0] = 1 ;
}
int get_fail ( int x ) {//和KMP一样,失配后找一个尽量最长的
while ( S[n - len[x] - 1] != S[n] ) x = fail[x] ;
return x ;
}
int add ( int c ) {
c -= 'a' ;
S[++ n] = c ;
int cur = get_fail ( last ) ;//通过上一个回文串找这个回文串的匹配位置
if ( !next[cur][c] ) {//如果这个回文串没有出现过,说明出现了一个新的本质不同的回文串
int now = newnode ( len[cur] + 2 ) ;//新建节点
fail[now] = next[get_fail ( fail[cur] )][c] ;//和AC自动机一样建立fail指针,以便失配后跳转
next[cur][c] = now ;
num[now] = num[fail[now]] + 1 ;
}
last = next[cur][c] ;
cnt[last] ++ ;
return len[last];
}
void count () {
for ( int i = p - 1 ; i >= 0 ; -- i ) cnt[fail[i]] += cnt[i] ;
//父亲累加儿子的cnt,因为如果fail[v]=u,则u一定是v的子回文串!
}
int dfs(int u){
sz[u]=1;
c[u]=0;
for(int t=u;!vis[t]&&t>1;t=fail[t]){
vis[t]=u;
c[u]++;
}
for(int i=0;i<26;i++){
if(next[u][i]==0) continue;
sz[u]+=dfs(next[u][i]);
}
for(int t=u;vis[t]==u&&t>1;t=fail[t]){
vis[t]=0;
}
return sz[u];
}
long long solve(){
dfs(0);dfs(1);
long long ans=0;
for(int i=2;i<p;i++){
ans+=1ll*sz[i]*c[i];
}
ans=ans-p+2;
return ans;
}
} ;
Palindromic_Tree T;
char s[MAXN];
int n;
int main(){
int ca=1;
int t;
scanf("%d",&t);
while(t--){
scanf("%s",s);
n=strlen(s);
T.init();
ll ans=0;
for(int i=0;i<n;i++){
T.add(s[i]);
}
printf("Case #%d: %lld\n",ca++,T.solve());
}
return 0;
}