题目链接:https://vjudge.net/contest/362376#problem/B
题意很好理解
解题思路:
一开始我的方法是利用双向KMP,求正向KMP和逆置后的KMP,然后分两种情况,分割点在前半段时,分割点在后半段时。
比如在前半段时,len=(n-1)/2,分割点为i,分两部分处理,i的前半部分在正常顺序的U串中,利用while循环使k=Next[len+1+i](复制的S串的前半部分),k=Next[k],往前推导直到Next[k]=i-1或者Next[k]=0,如果Next[k]=i-1则前半部分对应成功。
i后半部分,在逆置后的U’中进行,同理。
当时还沾沾自喜,以为找到巧妙的方法了,结果TLE了
超时做法代码:
#include<iostream>
#include<cstdio>
#include<string.h>
#include<string>
using namespace std;
#define maxn 2000100
char p[maxn];
char h[maxn];
char ans[maxn];
int n;
int cnt;
int len;
int hNext[maxn];
int pNext[maxn];
void get_next(char *pp,int *Next)
{
for (int i=2;i<=n;i++){
int j=Next[i-1];
while ((pp[j+1]!=pp[i])&&(j>0))
j=Next[j];
if (pp[j+1]==pp[i])
Next[i]=j+1;
else
Next[i]=0;
}
}
int main()
{
cin>>n;
len=(n-1)/2;
scanf("%s",p+1);
if(n%2==0)
{
cout<<"NOT POSSIBLE"<<endl;
return 0;
}
get_next(p,pNext);
for(int i=n;i>=1;i--)
h[n-i+1]=p[i];
get_next(h,hNext);
for(int i=1;i<=n;i++)
{
if(i>=len+1)
{
int flag=0;
int t1=n-i;
int t2=len-n+i;
int temp1=i-1;
int temp2=len+t1+1;
while(1)
{
if(pNext[temp1]==t2)
{
flag++;
break;
}
if(pNext[temp1]==0)
break;
temp1=pNext[temp1];
}
while(1)
{
if(hNext[temp2]==t1)
{
flag++;
break;
}
if(hNext[temp2]==0)
break;
temp2=hNext[temp2];
}
if(flag==2)
{
int f=0;
for(int i=1;i<=len;i++)
{
if(cnt==1&&ans[i]!=p[i])
f=1;
ans[i]=p[i];
}
if(cnt==1)
{
if(f==1)
cnt++;
}
else
cnt++;
}
}
if(i<len+1)
{
int flag=0;
int t1=i-1;
int t2=len-i+1;
int temp1=len+t2;
int temp2=len+t1+1;
while(1)
{
if(hNext[temp1]==t2)
{
flag++;
break;
}
if(hNext[temp1]==0)
break;
temp1=hNext[temp1];
}
while(1)
{
if(pNext[temp2]==t1)
{
flag++;
break;
}
if(pNext[temp2]==0)
break;
temp2=pNext[temp2];
}
if(flag==2)
{
int f=0;
for(int i=len+2;i<=n;i++)
{
if(cnt==1&&ans[i-len-1]!=p[i])
f=1;
ans[i-len-1]=p[i];
}
if(cnt==1)
{
if(f==1)
cnt++;
}
else
cnt++;
}
}
if(cnt>=2)
break;
}
if(cnt==1)
printf("%s\n",ans+1);
else if(cnt==0)
cout<<"NOT POSSIBLE"<<endl;
else
cout<<"NOT UNIQUE"<<endl;
return 0;
}
正解是利用Hash数组,也是讨论分割点的问题,只不过利用hash可以直接跨过分割点求出子字符串的Hash值,然后直接讨论计算比较就行。
跨分割点求两部分子串连在一起的hash值(i为分割点,连接1到i-1与i-1到len+1两部分):
#include<iostream>
#include<cstdio>
#include<string.h>
#include<string>
using namespace std;
#define maxn 2000100
#define ll unsigned long long
const int mod=1e9+7;
const int p=31;
char str[maxn];
int n;
int cnt;
int len;
ll Hash[maxn];
ll Pow[maxn];
ll ans;
ll res[maxn];
int L,R;
void get_hash()
{
Pow[0]=1;
for(int i=1;i<=maxn/2;i++)
Pow[i]=Pow[i-1]*p;
for(int i=1;i<=n;i++)
{
Hash[i]=(Hash[i-1]*p+(str[i]-'A'+1));
}
}
ll cmp_hash(int l,int r)
{
return Hash[r]-Hash[l-1]*Pow[r-l+1];
}
int main()
{
cin>>n;
scanf("%s",str+1);
if(n%2==0)
{
cout<<"NOT POSSIBLE"<<endl;
return 0;
}
len=(n-1)/2;
get_hash();
for(int i=1;i<=n;i++)
{
if(i<len+1)
{
ll temp1=cmp_hash(i+1,len+1)+cmp_hash(1,i-1)*Pow[len+1-i];
ll temp2=cmp_hash(len+2,n);
if(temp1==temp2)
{
if(cnt==1)
{
if(ans!=temp1)
cnt++;
}
else
{
cnt++;
ans=temp1;
L=len+2;
R=n;
}
}
}
else if(i==len+1)
{
ll temp1=cmp_hash(1,len);
ll temp2=cmp_hash(len+2,n);
if(temp1==temp2)
{
if(cnt==1)
{
if(ans!=temp1)
cnt++;
}
else
{
cnt++;
ans=temp1;
L=len+2;
R=n;
}
}
}
else
{
ll temp1=cmp_hash(1,len);
ll temp2=cmp_hash(i+1,n)+cmp_hash(len+1,i-1)*Pow[n-i];
if(temp1==temp2)
{
if(cnt==1)
{
if(ans!=temp1)
cnt++;
}
else
{
cnt++;
ans=temp1;
L=1;
R=len;
}
}
}
if(cnt==2)
break;
}
if(cnt==1)
{
for(int i=L;i<=R;i++)
printf("%c",str[i]);
cout<<endl;
}
else if(cnt==0)
cout<<"NOT POSSIBLE"<<endl;
else
cout<<"NOT UNIQUE"<<endl;
return 0;
}