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;
}

浙公网安备 33010602011771号