HDU6791. Tokitsukaze, CSL and Palindrome Game
设随机空串末尾添加 \(\Sigma\) 中的字符,第一次出现子串 \(S\) 的期望长度为 \(E(S)\)。
给定一个长度为 \(n\) 的串,\(q\) 次询问,每次查询它的两个回文子串 \(A,B\),比较 \(E(A),E(B)\) 的大小。
\(1\le n,q\le 10^5\)。
首先根据 \(\text{CTSC2006}\) 歌唱王国 的套路,有
于是只需比较询问的两个回文子串的 \(\text{Border}\)。
考虑一个回文串 \(\mathcal{S}\) 的 \(\text{Border}\),\(\mathcal{S}[1:l]=\mathcal{S}[|\mathcal{S}|-l+1:|\mathcal{S}|]\),且 \(\mathcal{S}_i=\mathcal{S}_{|\mathcal{S}|-i+1}\),所以 \(\mathcal{S}_{t}=\mathcal{S}_{t-|\mathcal{S}|+l}=\mathcal{S}_{2|\mathcal{S}|-t-l+1}\ (t\in [|\mathcal{S}|-l+1,|\mathcal{S}|])\),所以 \(\mathcal{S}[1:l],\mathcal{S}[|\mathcal{S}|-l+1:|\mathcal{S}|]\) 都是回文串。
那么就可以建出 \(\text{PAM}\) 通过跳 \(\text{Fail}\) 链找到所有的 \(\text{Border}\) 的长度,根据等比数列求和公式可以得到 \(|\Sigma|^{|T|}>\sum\limits_{0\le i<|T|} |\Sigma|^{i}\),所以贪心从长度大的 \(\text{Border}\) 开始比较,一旦有不同,拥有长度更大的 \(\text{Border}\) 的字符串期望更大。所以可以用倍增和哈希预处理出从节点 \(p\) 跳 \(2^k\) 次的路径中的 \(\text{Border}\) 长度连接起来的哈希值。然后询问时找出第一个不同的位置,比较 \(\text{Border}\) 长度的大小即可。
总时间复杂度 \(O((n+q)\log n)\)。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+5,B1=29,B2=31,mod1=1e9+7,mod2=1e9+9;
int T,n,q,pw1[N],pw2[N],F[N][18],H1[N][18],H2[N][18];char s[N];
struct Node{int son[26],fail,len;};
struct Palindrome_Automaton{
Node node[N];int cnt,last,pos[N];
inline void clear(){
memset(pos,0,sizeof pos);
memset(node,0,sizeof node),cnt=1,last=0;
node[0].fail=node[1].fail=1,node[1].len=-1;
}
inline int getfail(int x,int i){
while(i-node[x].len-1<0||s[i-node[x].len-1]!=s[i])x=node[x].fail;
return x;
}
inline void insert(char c,int i){
int x=getfail(last,i),ch=c-'a';
if(!node[x].son[ch]){
node[++cnt].len=node[x].len+2;
int u=getfail(node[x].fail,i);
node[cnt].fail=node[u].son[ch];
node[x].son[ch]=cnt;
}
pos[i]=last=node[x].son[ch];
}
}PAM;
inline int ask(int l,int p){
l=p-l+1,p=PAM.pos[p];
for(int i=17;~i;--i)if(PAM.node[F[p][i]].len>=l)p=F[p][i];
return p;
}
int main(){
pw1[0]=pw2[0]=1;
for(int i=1;i<N;++i)pw1[i]=(ll)pw1[i-1]*B1%mod1,pw2[i]=(ll)pw2[i-1]*B2%mod2;
for(scanf("%d",&T);T--;){
scanf("%d%s",&n,s+1);PAM.clear();
for(int i=1;i<=n;++i)PAM.insert(s[i],i);
for(int i=2;i<=PAM.cnt;++i){
F[i][0]=PAM.node[i].fail,H1[i][0]=H2[i][0]=PAM.node[i].len;
for(int j=1;j<=17;++j){
F[i][j]=F[F[i][j-1]][j-1];
if(F[i][j]>1){
H1[i][j]=((ll)H1[i][j-1]*pw1[1<<j-1]%mod1+H1[F[i][j-1]][j-1])%mod1;
H2[i][j]=((ll)H2[i][j-1]*pw2[1<<j-1]%mod2+H2[F[i][j-1]][j-1])%mod2;
}
}
}
for(scanf("%d",&q);q--;){
int l1,r1,l2,r2;
scanf("%d%d%d%d",&l1,&r1,&l2,&r2);
int A=ask(l1,r1),B=ask(l2,r2);
for(int i=17;~i;--i)if(F[A][i]>1&&F[B][i]>1&&H1[A][i]==H1[B][i]&&H2[A][i]==H2[B][i])
A=F[A][i],B=F[B][i];
A=max(PAM.node[A].len,0),B=max(PAM.node[B].len,0);
puts(A==B?"draw":A<B?"sjfnb":"cslnb");
}
}
}