P7861 SAVEZ 题解(哈希)
思路
题目中要求我们找出子序列 x x x 使得 x x x 最长且满足其中靠后的字符串都以它之前的字符串作为结尾,即下图所示:
我们需要比较字符串的开头和结尾是否相等且在之前出现过,因此我们使用哈希将开头和结尾存起来。
由于我们要的并不是回文串,所以我们需要将开头与结尾的哈希值用不同的方法分别求。
对于在开头的部分: h a s h = ( h a s h + h a s h p o w j × t r j ) hash = (hash+hashpow_j\times tr_j)%mod hash=(hash+hashpowj×trj),其中 h a s h p o w j hashpow_j hashpowj 表示进制数的 j j j 次方。
对于在结尾的部分: h a s h = ( h a s h × h a s h n u m + s t r j ) hash=(hash\times hashnum+str_j)%mod hash=(hash×hashnum+strj),其中 h a s h n u m hashnum hashnum 表示进制数。
接下来,为了确定开头和结尾的字符串在之前有没有出现过,我们用一个 map 存一下每个哈希值对应的最长的序列长度 d p h a s h dp_{hash} dphash。然后再取最大的 d p h a s h dp_{hash} dphash 再 + 1 +1 +1 就好啦!
我的代码用了双哈希避免哈希碰撞,但实测单哈希也能过。
代码
#include<bits/stdc++.h>
using namespace std;
const int hash_code1=114;
const int hash_code2=514;//太臭力!
const int mod=1000000007;
long long hasheachnum1[1000006];
long long hasheachnum2[1000006];
void geth()
{
hasheachnum1[0]=1;
hasheachnum2[0]=1;
for(int i=1;i<1000006;i++)//求hashpow[i]
{
hasheachnum1[i]=(hasheachnum1[i-1]*hash_code1)%mod;
hasheachnum2[i]=(hasheachnum2[i-1]*hash_code2)%mod;
}
return;
}
int main()
{
geth();
string loda;
int N,ans=0;
scanf("%d\n",&N);
map<pair<int,int>,int> dp;//经过实测,map里面不能塞struct作为下标,所以用pair存双哈希
for(int i=1;i<=N;i++)
{
cin>>loda;
long long hashx1=0,hashx2=0;//开头的哈希
long long hashy1=0,hashy2=0;//结尾的哈希
int l=loda.size(),temp=0;
for(int j=0;j<l;j++)
{
hashx1=(hashx1+hasheachnum1[j]*(int)loda[j])%mod;
hashx2=(hashx2+hasheachnum2[j]*(int)loda[j])%mod;
hashy1=(hashy1*hash_code1+(int)loda[l-j-1])%mod;
hashy2=(hashy2*hash_code2+(int)loda[l-j-1])%mod;
if(hashx1==hashy1 && hashx2==hashy2 && temp<dp[{hashx1,hashx2}])//如果前后的哈希值相等就更新dp值
temp=dp[{hashx1,hashx2}];
}
dp[{hashx1,hashx2}]=temp+1;
if(temp>=ans)
ans=temp+1;
}
printf("%d\n",ans);
}