您的位置:首页 > 其它

Codeforces #30E: Tricky and Clever Password 题解

2018-02-07 23:57 435 查看
首先可以发现一个结论
我们枚举一个中间回文串的中心点,会发现我们一定会选择以它为中心的最长的回文串
因为考虑左边右边的两个串,如果它们与长回文串不重叠,那么取长串显然更优;如果重叠,那么不重叠的部分仍然可以取,重叠的部分已经被我的长串取掉了,也不亏。所以取长串肯定最优
对于每个长度的后缀,如果前面有多个子串和它匹配,那么显然越靠前越优
我们先求对于每一个位置,能和后缀匹配的最长长度,记为p[i]
我们枚举位置以后二分长度,字符串哈希一发就好
然后我们维护一个数组minpos,minpos[i]表示长度为i的后缀最早在哪里出现过
我们从左往右扫描p数组,对于第i个位置,minpos[1]~minpos[p[i]]的答案都不会超过i
因为是从左往右扫描的,所以如果碰到已
4000
经填过的说明有更小的答案,就不用再往下更新了,这样每个格子最多被更新一次,复杂度是线性的
然后跑一趟manacher找出每个位置为中心的最长半径
然后枚举中心点,因为考虑到minpos[i]数组是单调递增的(因为条件越来越苛刻)所以可以二分左右串的长度,检查是否不与中间串重叠就好了
总复杂度O(nlogn)
#include <cstdio>
#include <iostream>
#include <cstring>
#include <string>
#include <cstdlib>
#include <utility>
#include <cctype>
#include <algorithm>
#include <bitset>
#include <set>
#include <map>
#include <vector>
#include <queue>
#include <deque>
#include <stack>
#include <cmath>
#define LL long long
#define LB long double
#define x first
#define y second
#define Pair pair<int,int>
#define pb push_back
#define pf push_front
#define mp make_pair
#define LOWBIT(x) x & (-x)
using namespace std;

const int MOD=1e9+7;
const LL LINF=2e16;
const int INF=2e9;
const int magic=348;
const double eps=1e-10;
const double pi=3.14159265;

inline int getint()
{
char ch;int res;bool f;
while (!isdigit(ch=getchar()) && ch!='-') {}
if (ch=='-') f=false,res=0; else f=true,res=ch-'0';
while (isdigit(ch=getchar())) res=res*10+ch-'0';
return f?res:-res;
}

char ss[300048];
char s[300048];int len;

int RL[300048],maxlen[300048];

int minpos[100048];
LL hsh1[100048],hsh2[100048];
LL hsh3[100048],hsh4[100048];
LL fp1[100048],fp2[100048];
const int p=998244353;
const int MOD1=19260817,MOD2=1e9+7;

void init_hash()
{
int i;
fp1[0]=fp2[0]=1;
for (i=1;i<=len;i++) fp1[i]=(fp1[i-1]*p)%MOD1,fp2[i]=(fp2[i-1]*p)%MOD2;
for (i=1;i<=len;i++) hsh1[i]=(hsh1[i-1]*p+s[i])%MOD1,hsh2[i]=(hsh2[i-1]*p+s[i])%MOD2;
for (i=len;i>=1;i--) hsh3[i]=(hsh3[i+1]*p+s[i])%MOD1,hsh4[i]=(hsh4[i+1]*p+s[i])%MOD2;
}

Pair gethash1(int left,int right)
{
pair<LL,LL> res;
res.x=((hsh1[right]-(hsh1[left-1]*fp1[right-left+1])%MOD1)%MOD1+MOD1)%MOD1;
res.y=((hsh2[right]-(hsh2[left-1]*fp2[right-left+1])%MOD2)%MOD2+MOD2)%MOD2;
return res;
}

Pair gethash2(int left,int right)
{
pair<LL,LL> res;
res.x=((hsh3[left]-(hsh3[right+1]*fp1[right-left+1])%MOD1)%MOD1+MOD1)%MOD1;
res.y=((hsh4[left]-(hsh4[right+1]*fp2[right-left+1])%MOD2)%MOD2+MOD2)%MOD2;
return res;
}

int main ()
{
int i,j;
scanf("%s",s+1);len=strlen(s+1);
int cur=1;
for (i=1;i<=len;i++) ss[cur++]='#',ss[cur++]=s[i];
ss[cur]='#';
int llen=cur,maxright=0,mpos=0;
for (i=1;i<=llen;i++)
{
if (i<maxright)
RL[i]=min(RL[2*mpos-i],maxright-i);
else
RL[i]=1;
while (i-RL[i]>=1 && i+RL[i]<=llen && ss[i-RL[i]]==ss[i+RL[i]]) RL[i]++;
if (i+RL[i]>maxright)
{
maxright=i+RL[i];
mpos=i;
}
}
init_hash();
for (i=1;i<=llen;i++)
if (ss[i]!='#') maxlen[i/2]=RL[i]-1;
for (i=1;i<=len;i++)
{
int maxl=0,l=1,r=len,mid;
while (l<=r)
{
mid=(l+r)>>1;
if (gethash1(i,i+mid-1)==gethash2(len-mid+1,len))
{
maxl=mid;
l=mid+1;
}
else
{
r=mid-1;
}
}
if (maxl)
{
for (j=maxl;j>=1 && !minpos[j];j--) minpos[j]=i;
}
}
int anslen=0;
Pair ans1=mp(-1,-1),ans2=mp(-1,-1),ans3=mp(-1,-1);
for (i=1;i<=len;i++)
{
if (maxlen[i]>anslen)
{
anslen=maxlen[i];
ans1=ans3=mp(-1,-1);ans2=mp(i-(maxlen[i]-1)/2,i+(maxlen[i]-1)/2);
}
int l=1,r=len,maxl=0,mid;
while (l<=r)
{
mid=(l+r)>>1;
if (len-mid+1>i+(maxlen[i]-1)/2 && minpos[mid] && minpos[mid]+mid-1<i-(maxlen[i]-1)/2)
{
maxl=mid;
l=mid+1;
}
else
{
r=mid-1;
}
}
if (maxl)
{
if (maxl+maxl+maxlen[i]>anslen)
{
anslen=maxl+maxl+maxlen[i];
ans1=mp(minpos[maxl],minpos[maxl]+maxl-1);
ans2=mp(i-(maxlen[i]-1)/2,i+(maxlen[i]-1)/2);
ans3=mp(len-maxl+1,len);
}
}
}
if (ans1.x==-1)
{
printf("1\n");
printf("%d %d\n",ans2.x,ans2.y-ans2.x+1);
}
else
{
printf("3\n");
printf("%d %d\n",ans1.x,ans1.y-ans1.x+1);
printf("%d %d\n",ans2.x,ans2.y-ans2.x+1);
printf("%d %d\n",ans3.x,ans3.y-ans3.x+1);
}
return 0;
}
尝试优化这个做法
首先对于求minpos[i]的过程,如果我们把原字符串反过来接在原串前面,跑一遍KMP,那么fail数组对应的就是我需要的minpos
于是这个部分可以做到O(n)
考虑最后统计答案的部分
我们预处理数组f[i]表示如果中间的回文串的左端点在i,单考虑左边可以取到的最大长度
因为minpos是单调的,所以我们仍然可以使用打标记的方法,只给没有打标记的格子更新答案,每个格子最多被更新一次,复杂度是线性的
最后枚举中心点的时候,f[i]和右边的长度取一个min就好
总复杂度O(n)
#include <cstdio>
#include <iostream>
#include <cstring>
#include <string>
#include <cstdlib>
#include <utility>
#include <cctype>
#include <algorithm>
#include <bitset>
#include <set>
#include <map>
#include <vector>
#include <queue>
#include <deque>
#include <stack>
#include <cmath>
#define LL long long
#define LB long double
#define x first
#define y second
#define Pair pair<int,int>
#define pb push_back
#define pf push_front
#define mp make_pair
#define LOWBIT(x) x & (-x)
using namespace std;

const int MOD=1e9+7;
const LL LINF=2e16;
const int INF=2e9;
const int magic=348;
const double eps=1e-10;
const double pi=3.14159265;

inline int getint()
{
char ch;int res;bool f;
while (!isdigit(ch=getchar()) && ch!='-') {}
if (ch=='-') f=false,res=0; else f=true,res=ch-'0';
while (isdigit(ch=getchar())) res=res*10+ch-'0';
return f?res:-res;
}

int len,slen;
char s[300048],ss[300048];
int RL[300048],maxright,pos,maxlen[300048],mlen[300048],minpos[300048];
int fail[300048];
int f[300048];

void getfail()
{
int k=-1,j=0;
fail[0]=-1;
while (j<=slen)
{
if (k==-1 || ss[j+1]==ss[k+1])
fail[++j]=++k;
else
k=fail[k];
}
}

void getRL()
{
memset(ss,'\0',sizeof(ss));
int i,cur=1;
for (i=1;i<=len;i++)
{
ss[cur++]='#';
ss[cur++]=s[i];
}
ss[cur]='#';slen=cur;
maxright=pos=0;
for (i=1;i<=slen;i++)
{
if (i<maxright)
RL[i]=min(maxright-i,RL[2*pos-i]);
else
RL[i]=1;
while (i-RL[i]>=1 && i+RL[i]<=slen && ss[i-RL[i]]==ss[i+RL[i]]) RL[i]++;
if (i+RL[i]>maxright)
{
maxright=i+RL[i];
pos=i;
}
}
}

int main ()
{
//freopen ("30e.in","r",stdin);
//freopen ("30e.out","w",stdout);
int i,j;
scanf("%s",s+1);len=strlen(s+1);
for (i=1;i<=len;i++) ss[i]=s[len-i+1];
ss[len+1]='$';
for (i=len+2;i<=len+2+len-1;i++) ss[i]=s[i-len-1];
slen=strlen(ss+1);
getfail();
for (i=len+2;i<=slen;i++) mlen[i-len-1-fail[i]+1]=max(mlen[i-len-1-fail[i]+1],fail[i]);
for (i=1;i<=len;i++)
for (j=mlen[i];j>=1 && !minpos[j];j--)
minpos[j]=i;
getRL();
//return 0;
for (i=1;i<=slen;i++) if (ss[i]!='#') maxlen[i/2]=RL[i]-1;
//cout<<"*"<<endl;
for (i=len;i>=1;i--)
if (minpos[i])
for (j=minpos[i]+i;j<=len && !f[j];j++)
f[j]=i;
//return 0;
Pair ans1=mp(-1,-1),ans2=mp(-1,-1),ans3=mp(-1,-1);int anslen=0;
for (i=1;i<=len;i++)
{
int ll=i-(maxlen[i]-1)/2,rr=i+(maxlen[i]-1)/2;
int add=min(len-rr,f[ll]);
if (maxlen[i]+add+add>anslen)
{
anslen=maxlen[i]+add+add;
ans2=mp(i-(maxlen[i]-1)/2,maxlen[i]);
if (add)
{
ans1=mp(minpos[add],add);
ans3=mp(len-add+1,add);
}
else
ans1=ans3=mp(-1,-1);
}
}
if (ans1.x==-1)
{
printf("1\n");
printf("%d %d\n",ans2.x,ans2.y);
}
else
{
printf("3\n");
printf("%d %d\n",ans1.x,ans1.y);
printf("%d %d\n",ans2.x,ans2.y);
printf("%d %d\n",ans3.x,ans3.y);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: