2021CCPC女生专场 F. 地图压缩 子串hash+二维KMP

https://codeforces.com/gym/103389/problem/F
做这个题之前要做一下 p o j 2185 poj2185 poj2185,这两个题很像,但是这个题有多次询问,所以如果每次都去计算一次所给矩形的最小循环节,如果对字符串进行逐个字符的比较,那么复杂度是 O ( n 2 q ) O(n^2q) O(n2q),不行,所以想到哈希,使用子串哈希的方法,使用字符串的哈希值来进行 k m p kmp kmp,这样复杂度就降到了 O ( n 2 + n q ) O(n^2+nq) O(n2+nq)

  • 进制 h a s h hash hash,如果设原来的字符串为 s s s,如果字符串都是小写字母,那么有 h a s h [ i ] = h a s h [ i − 1 ] × b a s e + s [ i ] − ′ a ′ hash[i]=hash[i-1]\times base + s[i]-'a' hash[i]=hash[i1]×base+s[i]a p [ i ] = p [ i − 1 ] × b a s e , ( p [ 0 ] = 1 ) p[i]=p[i-1]\times base,(p[0]=1) p[i]=p[i1]×base,(p[0]=1)其中 b a s e base base可取 13131 13131 13131,这里减不减 ′ a ′ 'a' a都行,如果想得到一个子串的 h a s h hash hash值,那么如果子串是 [ l , r ] [l,r] [l,r],那么有子串哈希值为 h a s h [ r ] − h a s h [ l − 1 ] × p [ r − l + 1 ] hash[r]-hash[l-1]\times p[r-l+1] hash[r]hash[l1]×p[rl+1]
#include <bits/stdc++.h>

using namespace std;

typedef unsigned long long ull;
ull base = 13131;
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int n, q;
    cin >> n >> q;
    vector<vector<char> > mp(n + 1, vector<char> (n + 1));
    vector<vector<ull> > row(n + 1, vector<ull>(n + 1)), col(n + 1, vector<ull>(n + 1));
    vector<ull> p(n + 1);
    p[0] = 1;
    for(int i=1;i<=n;i++){
        p[i] = p[i - 1] * base;
        for(int j=1;j<=n;j++){
            cin >> mp[i][j];
            row[i][j] = row[i][j - 1] * base + mp[i][j] - 'a';
            col[i][j] = col[i - 1][j] * base + mp[i][j] - 'a';
        }
    }
    while(q--){
        int x1, x2, y1, y2;
        cin >> x1 >> y1 >> x2 >> y2;
        int res = x2 - x1 + 1;
        vector<int> Next(res + 1);
        vector<ull> a(res + 1);
        for(int i=x1;i<=x2;i++){
            function<ull(int, int)> Get_Row = [&](int l, int r){
                return row[i][r] - row[i][l - 1] * p[r - l + 1];
            };
            a[i - x1] = Get_Row(y1, y2);
        }
        for(int i=1;i<res;i++){
            int j = Next[i];
            while(j && a[i] != a[j]) j = Next[j];
            Next[i + 1] = (a[i] == a[j] ? j + 1 : 0);
        }
        int x = res - Next[res];
        res = y2 - y1 + 1;
        Next.resize(res + 1);
        Next[1] = 0;
        a.resize(res + 1);
        for(int i=y1;i<=y2;i++){
            function<ull(int, int)> Get_Col = [&](int l, int r){
                return col[r][i] - col[l - 1][i] * p[r - l + 1];
            };
            a[i - y1] = Get_Col(x1, x2);
        }
        for(int i=1;i<res;i++){
            int j = Next[i];
            while(j && a[i] != a[j]) j = Next[j];
            Next[i + 1] = (a[i] == a[j] ? j + 1 : 0);
        }
        int y = res - Next[res];
        cout << x * y << '\n';
    }
    return 0;
}

2022.10.6日备注一下:不要用这种自然溢出,如果数据够硬会被生日攻击卡掉,用双模哈希比较稳

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Clarence Liu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值