P7861 [COCI2015-2016#2] SAVEZ 题解

做这题的心路历程十分神奇。

看到题,想到哈希匹配。

对于每一个 \(x_i\) 若可以为 \(x_j\) 的前缀和后缀,处理 \(x_i\) 的哈希值,求 \(x_j\) 的前缀和后缀哈希即可。

然后本机上跑不过,就进行了分裂循环,优化调用 map 次数等等一系列优化。

结果发现数据比我想象中的水的多。

还是放一下第一次做的代码:代码

\(dp_i\) 代表以第 \(i\) 个字符结尾的字序列长度最大值。

然后利用 map 存储之前每个字符串的哈希值,最后如果本次前缀和后缀都能匹配上之前的哈希值,那么可以进行转移。

\(dp_i=\max(dp_j+1)\hspace{0.1cm}\text{(初始}\hspace{0.1cm}dp_i=1)\)

最后求 \(\max(dp_i)\) 即可。

复杂度 \(O(\sum_{i=1}^{n}x_i\times\log(n))\)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<map>
#define int long long
using namespace std;
const int N=2e6+5;
const int mod1=1e15+37;
const int mod2=212370440130137957ll;
map<int,int>vis1,vis2;
int n,dp[N];
char s[N];
inline void read()
{
	int cnt=0;
	char ch=getchar();
	while(ch==' '||ch=='\n')ch=getchar();
	while(ch!=' '&&ch!='\n')
	{
		s[++cnt]=ch;
		ch=getchar();
	}
	s[++cnt]='\0';
}
signed main()
{
	//freopen("savez.in","r",stdin);
	//freopen("savez.out","w",stdout);
	scanf("%lld",&n);
	int ans=0;
	for(int i=1;i<=n;i++)
	{
		scanf("%s",s+1);
		int len=strlen(s+1);
		int num1=0,num2=0,lim1=1,lim2=1;
		int num3=0,num4=0;
		dp[i]=1;
		for(int j=1;j<=len;j++)
		{
			num1=num1+(s[j]-'A'+2)*lim1;
			num1%=mod1;
			num2=num2+(s[j]-'A'+2)*lim2;
			num2%=mod2;
			num3=num3*30+(s[len-j+1]-'A'+2);
			num3%=mod1;
			num4=num4*30+(s[len-j+1]-'A'+2);
			num4%=mod2;
			lim1*=30;
			lim1%=mod1;
			lim2*=30;
			lim2%=mod2;
			int _1=vis1[num1],_2=vis2[num2],_3=vis1[num3],_4=vis2[num4];
			if(_1&&_3&&_1==_2&&_2==_3&&_3==_4)dp[i]=max(dp[i],dp[_1]+1);
		}
		vis1[num1]=i,vis2[num2]=i;
		ans=max(ans,dp[i]);
	}
	printf("%lld",ans);
	return 0;
}
posted @ 2023-02-24 13:30  Gmt丶Fu9ture  阅读(26)  评论(0)    收藏  举报