题面:
题意:
给定一个串
S
S
S ,从中选出两个回文子串。
令
E
(
s
)
E(s)
E(s) 表示从一个空串
P
P
P 中不断随机插入新字符直到
s
s
s 第一次出现在
P
P
P 中的时
P
P
P 的期望长度。
多次询问要你比较选出的两个子串回文子串的期望大小。
官方题解:
主要是:
令
a
i
a_i
ai 表示
A
[
1
,
i
]
A[1,i]
A[1,i] 是否是
A
A
A 的一个
b
o
r
d
e
r
border
border,
a
i
a_i
ai为 1 表示是,0 表示不是。
那么
E
(
A
)
=
∑
i
=
1
L
a
i
∗
m
i
E(A)=\sum_{i=1}^La_i*m^i
E(A)=∑i=1Lai∗mi。
此结论不知道而且不易证出。
由上述结论可知,本题只需要比较
S
a
.
.
b
S_{a..b}
Sa..b与
S
c
.
.
.
d
S_{c...d}
Sc...d 的
b
o
r
d
e
r
border
border 长度由大到小排序后的字典序即可。
回文串的
b
o
r
d
e
r
border
border都是回文串。那么找到
S
a
.
.
b
S_{a..b}
Sa..b与
S
c
.
.
.
d
S_{c...d}
Sc...d在回文自动机上面对应的节点,沿着 fail 指针跳到根,所经过每个节点都是他们的
b
o
e
d
e
r
boeder
boeder。每个节点到根最多是 logn 个等差数列,那么总共花费 logn 的时间即可比较大小,其中我们还需要花费 logn 的时间倍增找到找到
S
a
.
.
b
S_{a..b}
Sa..b与
S
c
.
.
.
d
S_{c...d}
Sc...d在回文自动机上面对应的节点。
时间复杂度 O ( n l o g n ) O(nlogn) O(nlogn)
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<string>
#include<queue>
#include<bitset>
#include<map>
#include<unordered_map>
#include<set>
#include<list>
#define ui unsigned int
#define ll long long
#define llu unsigned ll
#define ld long double
#define pr make_pair
#define pb push_back
#define lc (cnt<<1)
#define rc (cnt<<1|1)
//#define len(x) (t[(x)].r-t[(x)].l+1)
#define tmid ((l+r)>>1)
#define max(x,y) ((x)>(y)?(x):(y))
#define min(x,y) ((x)>(y)?(y):(x))
using namespace std;
const int inf=0x3f3f3f3f;
const ll lnf=0x3f3f3f3f3f3f3f3f;
const double dnf=1e18;
const int mod=1e9+7;
const double eps=1e-5;
const double pi=acos(-1.0);
const int hp=13331;
const int maxn=1000100;
const int maxp=1100;
const int maxm=4000100;
const int up=1000;
const int N=26;
struct Palindromic_Tree
{
int t[maxn][N];
int fail[maxn];
int cnt[maxn];
int num[maxn];
int len[maxn];
int S[maxn];
int di[maxn];//节点x与fail[x]长度之差
int pre[maxn];//上一个等差序列的最后一项。
int pos[maxn];
int f[maxn][20];
int ft;
int last;
int n;
int p;
int newnode(int l)
{
for(int i=0;i<N;++i) t[p][i]=0;
cnt[p]=0;
num[p]=0;
len[p]=l;
return p++;
}
void init(void)
{
p=0;
newnode(0);
newnode(-1);
last=0;
n=0;
S[n]=-1;
fail[0]=1;
pre[0]=1;
}
int get_fail(int x)
{
while(S[n-len[x]-1]!=S[n]) x=fail[x];
return x;
}
void add(int c,int i)
{
c-='a';
S[++n]=c;
int cur=get_fail(last);
if(!t[cur][c])
{
int now=newnode(len[cur]+2);
fail[now]=t[get_fail(fail[cur])][c];
t[cur][c]=now;
num[now]=num[fail[now]]+1;
di[now]=len[now]-len[fail[now]];
pre[now]=(di[now]==di[fail[now]])?pre[fail[now]]:fail[now];
}
last=t[cur][c];
pos[i]=last;
cnt[last]++;
}
void build_fail_tree(void)
{
ft=log2(n)+1;
for(int i=2;i<p;i++)
{
memset(f[i],0,sizeof(f[i]));
f[i][0]=fail[i];
for(int j=1;j<=ft;j++)
f[i][j]=f[f[i][j-1]][j-1];
}
}
int get(int x,int le)
{
x=pos[x];
for(int i=ft;i>=0;i--)
{
if(len[f[x][i]]>=le) x=f[x][i];
}
return x;
}
int cmp(int x,int y)
{
while(x>=2&&y>=2)
{
if(len[x]!=len[y]) return len[x]<len[y]?-1:1;
else if(di[x]!=di[y]) return di[x]>di[y]?-1:1;
x=pre[x],y=pre[y];
}
if(x<=2&&y<=2) return 0;
else if(x<=2) return -1;
else return 1;
}
void _count(void)
{
for(int i=p-1;i>=0;--i) cnt[fail[i]]+=cnt[i];
}
}pam;
int n,q;
char str[maxn];
int main(void)
{
int tt;
scanf("%d",&tt);
while(tt--)
{
scanf("%d%s",&n,str+1);
pam.init();
for(int i=1;i<=n;i++)
pam.add(str[i],i);
pam.build_fail_tree();
int a,b,c,d;
scanf("%d",&q);
for(int i=1;i<=q;i++)
{
scanf("%d%d%d%d",&a,&b,&c,&d);
int op=pam.cmp(pam.get(b,b-a+1),pam.get(d,d-c+1));
if(op==0) printf("draw\n");
else if(op==-1) printf("sjfnb\n");
else printf("cslnb\n");
}
}
return 0;
}