hdu6138 hash+二分

题意: n个串,每次查询两个,问最长的公共自串并且这个子串要是n个串其中一个的前缀。


思路:正解是AC自动机。。。这里瞎搞了一下居然过了,直接二分答案,然后判断(类似hash求公共子串一样),就再加一个是不是前缀的判断就好。。

时间复杂度理论上最差n*m*logn。。。但是神奇的只跑了150ms。。。


代码:

#include<bits/stdc++.h>
using namespace std;
#define MEM(a,b) memset(a,b,sizeof(a))
#define bug puts("bug");
#define PB push_back
#define MP make_pair
#define X first
#define Y second
typedef unsigned long long ll;
typedef pair<int,int> pii;
const int maxn=1e5+10;
const int mod=1e9+7;
using namespace std;
int t,m,n,k;
string s[maxn];
const unsigned long long SEED = 13331;
unsigned long long P[maxn],S[maxn];
unsigned long long HS(int l,int r,int x){
    if(l==r) return s[x][l-1];
    return S[r] - S[l-1]*P[r-l+1];
}
unordered_set<unsigned long long> st,tmp;
bool ok(int len,int x,int y){
    tmp.clear();
    for(int i=1;i<=s[x].size();i++)
        S[i] = S[i-1]*SEED + s[x][i-1];
    for(int i=1;i+len-1<=s[x].size();i++)
        tmp.insert(HS(i,i+len-1,x));
    for(int i=1;i<=s[y].size();i++)
        S[i] = S[i-1]*SEED + s[y][i-1];
    for(int i=1;i+len-1<=s[y].size();i++)
        if(tmp.count(HS(i,i+len-1,y))&&st.count(HS(i,i+len-1,y)))
            return true;
    return false;
}

int main(){
    P[0] = 1;S[0]=0;
    for(int i = 1; i < maxn; i++)P[i] = P[i-1] * SEED;
    ios::sync_with_stdio(false);
    cin>>t;
    while(t--){
        st.clear();
        cin>>n;
        for(int i=0;i<n;i++) cin>>s[i];
        for(int i=0;i<n;i++)
            for(int j=1;j<=s[i].size();j++)
                S[j] = S[j-1]*SEED + s[i][j-1],st.insert(S[j]);
        cin>>m;
        int x,y;
        for(int i=0;i<m;i++){
            cin>>x>>y;
            --x;--y;
            int l=0,r=min(s[x].size(),s[y].size());
            while(l<=r){
                int mid=(l+r)/2;
                if(ok(mid,x,y)) l=mid+1;
                else r=mid-1;
            }
            cout<<r<<endl;
        }
    }
    return 0;
}


posted @ 2017-08-17 20:17  zhangxianlong  阅读(86)  评论(0编辑  收藏  举报