[USACO5.1]乐曲主题Musical Themes

https://www.luogu.org/problemnew/show/P2743

第一次写双哈希

提供一个也许能过n <= 20000的哈希做法

常规套路,我们求出原串哈希值,然后二分答案mid,依次枚举每个位置起始,长为mid的串是否出现过,然后用map维护串的上一次的开始位置,复杂度O(nlog^2n)

对于转调,处理原数组的差分数组即可

然后这里有一个问题,哈希值容易重复。根据某(其实是我忘了叫啥的概率模型):值域在[0,mod]的哈希值,对于[0,20000]次的询问,冲突的概率其实很大,只是因为luogu上数据小,所以能过

解决这一问题的方法是双哈希。选择两个模数作为哈希,开两个map分别维护两个哈希值,map里维护<ll,pair>,pair里存位置和这个点的另一个哈希值。然后在算某个位置的哈希值的时候呢,你将两个哈希值都算出来,哪个能够和两个都匹配上,哪个就是正确的

这样的出错概率就很小了

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<cmath> 
#include<iostream>
#include<map>
using namespace std;
#define O(x) cout << #x << " " << x << endl;
#define O_(x) cout << #x << " " << x << "  ";
#define B cout << "breakpoint" << endl;
#define pii pair<ll,int>
#define mp make_pair
typedef double db;
typedef long long ll;
inline int read()
{
    int ans = 0,op = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9')
    {
        if(ch == '-') op = -1;
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9')
    {
        (ans *= 10) += ch - '0';
        ch  = getchar();
    }
    return ans * op;
}
const int maxn = 5005;
const ll b = 5205;
const ll mod0 = 998244353;
const ll mod1 = 19260817;
ll hash[maxn][2],base[maxn][2];
int a[maxn],val[maxn];
int n;
bool check(int l)
{
    l--;
    map<ll,pii> m0,m1;
    for(int i = 1;i <= n - l;i++)
    {
        ll tp0 = (hash[i + l - 1][0] - hash[i - 1][0] * base[l][0] % mod0 + mod0) % mod0;
        ll tp1 = (hash[i + l - 1][1] - hash[i - 1][1] * base[l][1] % mod1 + mod1) % mod1;
        if(m0[tp0].second)
        {
            pii u0 = m0[tp0];
            ll val0 = u0.first,lt = u0.second;
            if(val0 == tp1 && i - lt >= l + 1) return 1;
        }
        if(m1[tp1].second)
        {
            pii u1 = m1[tp1];
            ll val1 = u1.first,lt = u1.second;
            if(val1 == tp0 && i - lt >= l + 1) return 1;
        }
        if(!m1[tp1].second && !m0[tp0].second) 
        {
            m0[tp0] = mp(tp1,i);
            m1[tp1] = mp(tp0,i);
        }
    }
    return 0;
}
int main()
{
    n = read();
    base[0][0] = base[0][1] = 1;
    for(int i = 1;i <= n;i++)
        val[i] = read();
    for(int i = 1;i < n;i++)
    {
        a[i] = val[i + 1] - val[i] + 88;
        base[i][0] = (base[i - 1][0] * b) % mod0;
        hash[i][0] = (hash[i - 1][0] * b % mod0 + (ll)a[i]) % mod0;
        base[i][1] = (base[i - 1][1] * b) % mod1;
        hash[i][1] = (hash[i - 1][1] * b % mod1 + (ll)a[i]) % mod1;
    }
    int l = 5,r = n;
    int ans = 0;
    while(l < r)
    {
        int mid = l + r >> 1;
        if(check(mid))
            l = mid + 1,ans = max(ans,mid);
        else r = mid;
    }
    printf("%d",ans);
}
        
View Code
原文地址:https://www.cnblogs.com/LM-LBG/p/10907522.html