题意:
给出一个由小写字母组成的字符串
S
,问有多少由小写字母
构成的字条串
1:
|S|′=|S|
2:
S′[L..R]
是回文串,当且仅当
S[L..R]
是回文串
题解:
http://foreseeable97.logdown.com/posts/194507-herbicidalontak2010palindromic-equivalence
首先manacher合并同类串很简单,关键是染色,如果是一般的无向图,很显然是一个NP完全问题,证明这张图是一张弦图:
设 i<j<k
然后我们已经知道了 ai!=ak
那么我们要证明如果有 ai!=aj ,那么 aj!=ak
首先根据连边的策略来看……
ai!=ak ,说明 [i+1,k−1] 是回文串
所以 [k−j+i+1,k−1] 与 [i+1,j−1] 恰好成镜像关系
所以 [k−j+i+1,k−1] 也是回文串
那么根据回文关系…… a[k−j+i]=a[j]
那么 a[k−j+i] 和 a[k] 的关系只有两种可能
要么 a[k−j+i]!=a[k]
那么我们就得到 a[k]!=a[j] ,得证命题
否则 a[k−j+i]=a[k]
那么 a[j]=a[k]
j 和k 是在同一个联通块内
那么每一个联通块都是一个完全图!也就是一个弦图!
弦图的染色方案数是可以使用完美消除序列在 O(n) 的时间复杂度内解决的
有因为这题是完全图,所以任意一个 1∼n 的排列都是完美消除序列
于是直接从 1→n 进行计算即可
#include<bits/stdc++.h>
using namespace std;
const int Maxn=1e6+50,mod=1e9+7;
typedef pair<int,int> pii;
int tot,n,m,r[Maxn*2],fa[Maxn],ans=1;
vector <pii> E;
char ch[Maxn],s[Maxn*2];
inline int getf(int x){return (fa[x]==x)?x:(fa[x]=getf(fa[x]));}
struct Edge{
Edge* nxt;
int to;
Edge():nxt(NULL){}
}edge[Maxn*4];
int last[Maxn];
inline void merge(int i,int j)
{
if(i==j||i&1||j&1||!i||j>2*n)return;
fa[getf(i>>1)]=getf(j>>1);
}
inline void manacher()
{
static int mx,p;
mx=1,p=0;
for(int i=1;i<=tot;i++)
{
r[i]=(mx>i)?(min(mx-i,r[p*2-i])):(1);
while(s[i-r[i]]==s[i+r[i]])
{
merge(i-r[i],i+r[i]);
r[i]++;
}
if(i-r[i]>0&&i+r[i]<=2*n&&!((i-r[i])&1))E.push_back(make_pair(i+r[i],i-r[i]));
if(i+r[i]>mx)mx=i+r[i],p=i;
}
}
inline void add(int x,int y)
{
static int ecnt=0;
edge[++ecnt].nxt=(&edge[last[x]]);
edge[ecnt].to=y;
last[x]=ecnt;
}
int main()
{
scanf("%s",ch+1);
n=strlen(ch+1);
for(int i=1;i<=n;i++)s[++tot]='#',s[++tot]=ch[i],fa[i]=i;
s[0]='?';s[++tot]='#',s[++tot]='!';
manacher();
for(int e=E.size()-1;e>=0;e--)
{
int x=E[e].first>>1,y=E[e].second>>1;
add(getf(x),(getf(y)));
add(getf(y),(getf(x)));
}
for(int i=n;i>=1;i--)
{
if(getf(i)!=i)continue;
static int cnt,vis[Maxn],vt=0;
cnt=26;++vt;
for(Edge *e=&edge[last[i]];e!=(&edge[0]);e=e->nxt)
{
int v=e->to;
if(v<=i||vis[v]==vt)continue;
vis[v]=vt;--cnt;
}
ans=1ll*ans*cnt%mod;
}
printf("%d\n",ans);
}