1. 程式人生 > >bzoj2119: 股市的預測 Hash+二分 調和級數

bzoj2119: 股市的預測 Hash+二分 調和級數

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;
}