bzoj2119: 股市的预测 Hash+二分 调和级数

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/lvzelong2014/article/details/85683219

bzoj2119: 股市的预测

题目传送门

分析

差分之后就是求有多少段相距 B B 的相同子串。
一种思路是,考虑长度为 L L 的有多少种。
一个神仙做法是,每隔 L L 取一个关键点,那么每个长度为 L L 的子串最多只会覆盖一个关键点,考虑过某个关键点的答案,这样一定能做到不重不漏。
也就是求把一个定长度的区间两端过这个关键点 i i 左右滑动的最大范围。
l = i , r = i + B + L l=i,r=i+B+L
[ l , n ] , [ r , n ] [l,n],[r,n] 的lcs和 [ 1 , l 1 ] , [ 1 , r 1 ] [1,l-1],[1,r-1] 的lcp即可。
用后缀数组/自动机可以做到预处理 O ( n l o g ) O(nlog) 查询 O ( 1 ) O(1)
但博主比较懒,直接上了 H a s h Hash +二分。
复杂度 O ( ( n + n 2 +   ) l o g ) = O ( n l o g 2 ) O((n+\frac{n}{2}+\cdots)log)=O(nlog^2)

代码

#include<bits/stdc++.h>
typedef unsigned long long ULL;
const ULL se = 998244353; const int N = 5e4 + 10;
int ri() {
    char c = getchar(); int x = 0, f = 1; for(;c < '0' || c > '9'; c = getchar()) if(c == '-') f = -1;
    for(;c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) - '0' + c; return x * f;
}
ULL H[N], pw[N]; int n, B, s[N], Ans;
ULL Hash(int l, int r) {return H[r] - pw[r - l + 1] * H[l - 1];}
int lcp(int a, int b) {
    int l = 0, r = n - b, ans = -1;
    for(;l <= r;) {
        int m = l + r >> 1;
        Hash(a, a + m) == Hash(b, b + m) ? ans = m, l = m + 1 : r = m - 1;
    }
    return ans + 1;
}
int lcs(int a, int b) {
    int l = 0, r = a - 1, ans = -1;
    for(;l <= r;) {
        int m = l + r >> 1;
        Hash(a - m, a) == Hash(b - m, b) ? ans = m, l = m + 1 : r = m - 1;
    }
    return ans + 1;
}
void Solve(int L) {
    for(int i = 1;i + B + L <= n; i += L) 
    if(s[i] == s[i + B + L]) {
        int r = lcp(i, i + B + L), l = lcs(i, i + B + L);
        l = std::min(l, L); r = std::min(r, L);
        Ans += std::max(l + r - L, 0);
    }
}
int main() {
    //freopen(".in","r",stdin);
    //freopen(".out","w",stdout);
    n = ri(); B = ri();
    for(int i = 1;i <= n; ++i) {
        s[i] = ri();
        s[i - 1] = s[i] - s[i - 1];
    }
    --n;
    pw[0] = 1; for(int i = 1;i <= n; ++i) pw[i] = pw[i - 1] * se;
    for(int i = 1;i <= n; ++i) H[i] = H[i - 1] * se + s[i] % se;
    for(int L = 1;(L << 1) + B <= n; ++L) Solve(L);
    printf("%d\n", Ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/lvzelong2014/article/details/85683219