HYSBZ 3916 friends(跨段hash)

题目链接: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;
}
原创文章 65 获赞 3 访问量 2105

猜你喜欢

转载自blog.csdn.net/littlegoldgold/article/details/104966018