2021 Shandong Provincial Collegiate Programming Contest F. Birthday Cake(字符串哈希)

本文详细解析了Codeforces上的一道竞赛编程题目,涉及到字符串处理和哈希技巧。题目要求找出所有满足特定条件的字符串对,即两字符串首尾或尾首相接后,能被平分为两个相等的子串。解决方案中,通过哈希表统计相同长度字符串的数量,并处理不同长度字符串的匹配情况,最后计算出所有可能的组合。双模数哈希的使用确保了计算的准确性。
摘要由CSDN通过智能技术生成

Birthday Cake

https://codeforces.com/gym/103118/problem/F

题目大意:给出 n n n 个字符串,问有多少对无序 ( i , j ) (i,j) (i,j) 满足:将第 i i i 个字符串与第 j j j 个首尾或尾首拼接,可以使得,从新串中间劈一刀,可以得到两个相等的串(不允许翻转)。

我们发现,所有可以被统计进入答案的 p a i r pair pair 可以分成两类,第一,两个字符串长度相等,第二,两个字符串长度不等。相等的情况好处理,字符串哈希,然后用 m a p map map 统计一下就行。主要就是长度不等的情况,这时候我们考虑枚举每个字符串 i i i ,因为答案要统计的是无序对,那么要么我们要么统计其作为小串的所有答案,要么就统计其作为大串的所有答案。

对于长度不相等的两个串,要满足条件肯定长下图这样( S S S 串为短串, T T T 为长串)。假如我们将遍历到的字符串视其为短串,那么我们发现我们很难快速找到符合条件的长串。于是我们可以视其为长串寻找短串,那么我们只要遍历前后缀每个相等的部分,用 h a s h + m a p hash+map hash+map 查找下图中 1 1 1 部分字符串的个数,即是符合条件的短串个数,即是将遍历到的字符串视为长串得到的所有合理答案。那么利用此方法我们就可以统计所有两串长度不等的答案。

:这题最好运用双模数才不容易被卡。

在这里插入图片描述

#include <bits/stdc++.h>
 
using namespace std;
 
typedef long long ll;
 
const int N = 4e5 + 10;
const ll mod1 = 50331653, mod2 = 998244353, base = 233;
 
int n;
 
string s[N];
 
vector<ll> h1[N], h2[N];
 
map< pair<ll, ll>, int> cnt;
 
ll b1[N], b2[N];
 
pair<ll, ll> ghs(int i, int l, int r) {
    if (l > r) return {0, 0};
    ll num1 = (h1[i][r] - h1[i][l - 1] * b1[r - l + 1] % mod1 + mod1) % mod1;
    ll num2 = (h2[i][r] - h2[i][l - 1] * b2[r - l + 1] % mod2 + mod2) % mod2;
    return {num1, num2};
}
 
int main() {
#ifndef ONLINE_JUDGE
    freopen("in.txt", "r", stdin);
    freopen("out.txt", "w", stdout);
#endif
    b1[0] = b2[0] = 1;
    for (int i = 1; i <= 400000; ++i) {
        b1[i] = b1[i - 1] * base % mod1;
        b2[i] = b2[i - 1] * base % mod2;
    }
    scanf("%d", &n);
    for (int i = 1; i <= n; ++i) {
        cin >> s[i];
    }
    for (int i = 1; i <= n; ++i) {
        int L = s[i].length();
        h1[i].emplace_back(0), h2[i].emplace_back(0);
        for (int j = 0; j < L; ++j) {
            h1[i].emplace_back( (h1[i][j] * base % mod1 + s[i][j]) % mod1 );
            h2[i].emplace_back( (h2[i][j] * base % mod2 + s[i][j]) % mod2 );
        }
        cnt[{h1[i][L], h2[i][L]}]++;
    }
    long long ans = 0, t = 0;
    for (int i = 1; i <= n; ++i) {
        int L = s[i].length();
        t += cnt[{h1[i][L], h2[i][L]}] - 1;
        for (int len = 1; ; len++) {
            int q = L - len + 1;
            if (q - len < 2) break;
            if (ghs(i, 1, len) == ghs(i, q, L)) {
                ans += cnt[ghs(i, len + 1, q - 1)];
            }
        }
    }
    printf("%lld", ans + t / 2);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值