哈希求最长公共子串

给定 \(n\) 个字符串,长度总和不超过 \(m\),试求这 \(n\) 个字符串的最长公共子串长度。

\(1\le n,m\le 10^5\)

对于这种求最长公共子串的问题,我们可以用哈希解决。

不难发现,最长公共子串具有单调性,这提示我们二分答案

对于二分的长度 \(len\),考虑如何检验。

首先有一个非常暴力的想法,将所有字符串的长度为 \(len\) 的子串全部取出,如果发现其中一个子串在所有字符串中均出现过,那么检验成功。

具体而言,定义一个二元组 \(\{s,id\}\) 分别表示子串和所处字符串编号,然后进行去重,因为一个字符串可能贡献若干相同二元组,如 \(aaaaa\) 可以贡献 \(2\)\(\{aaaa,1\}\) 的二元组。

去重后,将所有子串相同的二元组放在一起,比如 \(\{aa,1\},\{aa,2\},\{aa,4\}...\) ,如果一个子串相等的连续段中有 \(n\) 个,那么即可说明所有字符串都可以出现该子串,进而说明长度 \(len\) 可行。

截止这里,我们发现整个算法最大的瓶颈就是子串的储存与快速比对,这里将字符串换成对应的哈希值即可。

至于如何将哈希值相等二元组放在一起,直接对二元组进行排序,去重即可。

P5546 [POI2000] 公共串

这就是一道典型的最长公共子串的题目,思路同上,下面给出代码。

#include<bits/stdc++.h>
using namespace std;

typedef long long LL;
typedef unsigned long long ULL;
#define PUI pair<ULL,int>
const int N=2100;
int n;
string s[6];
int len[6];
ULL p[N],ha[6][N];
vector<PUI> g;

ULL get(int id,int l,int r) {
    return ha[id][r]-ha[id][l-1]*p[r-l+1];
}

int check(int mid) {
    g.clear();
    for(int i=1;i<=n;i++) {
        for(int j=1;j+mid-1<=len[i];j++) {
            g.push_back({get(i,j,j+mid-1),i});
        }
    }
    sort(g.begin(),g.end());//排序
    g.erase(unique(g.begin(),g.end()),g.end());//去重
    int cnt=0; ULL nha=0;
    for(int i=0;i<g.size();i++) {
        if(cnt==0) {nha=g[i].first; cnt++;}
        else {
            if(nha==g[i].first) {
                cnt++;
                if(cnt==n) return 1;
            }
            else {
                cnt=1;
                nha=g[i].first;
            }
        }
    }
    return 0;
}

void Sol() {
    cin>>n;
    for(int i=1;i<=n;i++) {
        cin>>s[i]; len[i]=s[i].size(); s[i]=" "+s[i];
        for(int j=1;j<=len[i];j++) ha[i][j]=ha[i][j-1]*13331+int(s[i][j]-'a');
    }
    if(n==1) {
        cout<<len[1]; return ;
    }
    int l=0,r=max({len[1],len[2],len[3],len[4],len[5]});
    while(l<r) {
        int mid=l+r+1>>1;
        if(check(mid)) l=mid;
        else r=mid-1;
    }
    cout<<l<<endl;
}

int main(){
    p[0]=1;
    for(int i=1;i<=2000;i++) p[i]=p[i-1]*13331;
    Sol();
    return 0;
}
posted @ 2024-07-15 08:18  2017BeiJiang  阅读(34)  评论(0编辑  收藏  举报