2774:Long Long Message:字符串hash入门

题目大意

题目链接
给出两个很长的字符串S1,S2 ∣ S 1 , S 2 ∣ < 100000 |S1,S2|<100000 S1,S2<100000,求出S1,S2的最长的一个完全相同的连续序列的长度。

思路分析

显然是一道LCS的题目,但是不同点在于公共序列的连续,当然这个通过简单的修改LCS也是可以解决的,最大的问题在于字符串的长度,对于 O ( n 2 ) O(n^2) O(n2)的复杂度而言,显然是不能接受的。不过我们要最大化最长子串,二分法也许是个不错的选择,毕竟二分法在最大化最大值,最大化最小值这方面颇有建树。如果我们二分查找相应的长度,那么难点在于如何快速的判断我们是否有 m i d mid mid这么长的公共序列,此时就需要将比较短的那个字符串的每个长度为 m i d mid mid的子串进行hash

比较常见的方法是,将字符串看作是一个p进制的值,然后对这个很大的数进行取模,得到的就是该字符串的hash值,根据前人的经验,为了减少冲突,我们将p 选择为31 ,131 这些数字,并且为了方便,我们直接把数值类型定义为unsigned long long 这样,我们就不许要手动取模,当数值溢出的时候, unsigned long long 自然会对2的64次取模,然后就可以得到一串字符串的hash值了

#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<string>
#include<climits>
#include<cmath>
#define ULL unsigned long long
using namespace std;
 
string s1,s2;
int l1,l2,seed=131;
vector<ULL> hash;
bool judge(int x)
{
    hash.clear();//多组数据时不要忘了清空全局数组
    //构造s1串的hash表
    ULL tmp=0;
    for (int i = 0; i < x; i++)
    {
        tmp=tmp* seed + s1[i];
    }
    hash.push_back(tmp);
    ULL base =1;
    for (int i = 1; i < x; i++)//求出到达x的base值
    {
        base *= seed;
    }
    for (int i = x; i < l1; i++)
    {
        tmp=(tmp*seed+s1[i])-base*s1[i-x]*seed;
        hash.push_back(tmp);
    }
    //构造完毕
    sort(hash.begin(),hash.end()); //二分查找加速,必需先排序
    ULL hashval = 0;
    for (int i = 0; i < x; i++)//求出s2串0到x的hash值
    {
        hashval = hashval * seed + s2[i];
    }
    if (binary_search(hash.begin(),hash.end(),hashval))//查找s2串0到x的hash值是否在s1串的hash表中
        return 1;
    for (int i = x; i < l2; i++)//如果上面的s2串0到x的hash值未匹配成功,这儿接着匹配s2串长度为x的hash值是否出现在s1串的hash表中
    {
        hashval = hashval*seed+s2[i]-s2[i-x]*base*seed;
        if (binary_search(hash.begin(),hash.end(),hashval))
            return 1;
    }
    return 0;
}
int main()
{
    while (cin>>s1>>s2)
    {
        l1=s1.size();
        l2=s2.size();
        int ans = 0;
        int low=0,high = min(l1,l2);
        while (low <= high)//二分答案
        {
            int mid = (low+high)>>1;
            if (judge(mid))//判断答案是否可行
            {
                ans = mid;
                low = mid+1;
            }
            else
                high = mid-1;
        }
        printf("%d\n",ans);
    }
    return 0;
}
    
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值