相似的字串(hash+二分)

原题链接:相似的字串

题意:

\(给定长为n的字符串s,要取出k个位置不相交的字符串,取这k个串中任意两个最长公共前缀最小的最为 x\)

\(对所有符合条件的k,求出情况最大的x\)

样例:

image-20200413121600924

思路:

求相同前缀匹配方式可以通过 进制hash 来处理,同时对于符合条件的长度,使用二分来优化时间复杂度。

实际上这里就运用了 字串hash值的方式,我们设定数组 \(f[i]\) 表示已\(i\)为右端点的字串,然后每次截取从二分得到的\(x\)长度,记录相同子串的个数,如果个数大于给定的条件\(k\),则更新长度答案。

#include<bits/stdc++.h>
#define IOS ios::sync_with_stdio(0); cin.tie(0);
typedef unsigned long long ull;
using namespace std;
const int maxn = 2e5+5;
ull base = 131;
ull p[maxn],h[maxn];
int f[maxn];//代表以下标为右端点
char s[maxn];
int n,k;
unordered_map<ull,int> Last;
ull get_hash(int l,int r){
    return h[l] - h[r+1]*p[r-l+1];
}
bool check(int x){
    f[0] = 0;
    int res = 0;
    //枚举所有起点
    for(int i=1;i<=n-x+1;i++){
        if( i-x > 0)
            Last[get_hash(i-x,i-1)] = i-x;//记录最近出现的hash值位置(i-x为起点
        f[i] = f[Last[get_hash(i,i+x-1)]] + 1;//当前位置等于之前相同hash值的个数+1
        res = max(res,f[i]);
    }
 
    for(int i=1;i<=n-x+1;i++)
        Last[get_hash(i,i+x-1)] = 0;//清空
    //Last.clear();
    return res >= k;//个数是否超过k
}
int main(){
    IOS
    cin>>n>>k;
    cin>>(s+1);
    p[0] = 1;
    h[n+1] = 0;
    for(int i=1;i<=n;i++){
        h[n-i+1] = h[n-i+2]*base + s[n-i+1] - 'a';
        p[i] = p[i-1]*base;
    }
    int ans = 0;
    int l=1,r=n;//二分最大长度
    while(l<=r){
        int mid = (l+r)>>1;
        if(check(mid)){
            ans = max(ans,mid);
            l = mid + 1;
        }else{
            r = mid - 1;
        }
    }
    cout<<ans<<endl;
    return 0;
     
}
posted @ 2020-04-13 17:28  Tianwell  阅读(151)  评论(0编辑  收藏  举报