wangguan114514

博客园 首页 新随笔 联系 订阅 管理

洛谷CF1056E

前言

这是本蒟蒻第一篇题解,有什么不足之处欢迎各位大佬指出。

题目大意

给定一个仅包含 \(\tt 0\)\(\tt 1\) 的字符串 \(s\) 与一个仅包含英文小写字母的字符串 \(t\) ,给 \(s\) 中的每个 \(\tt 0\) 一个映射字符串 \(r_0\) 与每个 \(\tt 1\) 一个映射字符串 \(r_1\)\(r_0\)\(r_1\) 按照 \(s\)\(\tt 0\)\(\tt 1\) 的排列顺序进行排列所得到的字符串与 \(t\) 相同且不相等(注意审题),求一共存在多少组 \(r_0\)\(r_1\) 满足要求。保证 \(s\) 中至少有一个 \(\tt 0\)\(\tt 1\)\(2 \le \lvert s \rvert \le 10^5,1 \le \lvert t \rvert \le 10^6\)

思路

考虑枚举。首先枚举 \(r_0\)\(r_1\) 的长度 \(len_0\)\(len_1\) 与哈希值 \(h_0\)\(h_1\),接着逐个检验 \(h_0\)\(h_1\)\(t\) 的相应子串哈希值是否匹配,如果不匹配结束检验,如果全部匹配则总情况数加一,接着继续往后枚举。

\(s\)\(t\) 的长度分别为 \(n\)\(m\)\(s\)\(\tt 0\)\(\tt 1\) 的数量分别为 \(n_0\)\(n_1\)\(len_0\)\(len_1\) 满足以下关系:

\[len_0 \cdot \ n_0 + len_1 \cdot \ n_1 = m \]

将上式移项得:

\[len_0=\frac{m-n_1\cdot len_1}{n_0},len_1=\frac{m-n_0\cdot len_0}{n_1} \]

枚举的临界状态分别为 \(len_0=1\)\(len_1=1\),即 \(r_0\)\(r_1\) 分别仅有一个字符组成时的状态。我们从 \(len_0=1\) 开始枚举,则枚举的终点是 \(len_0=\dfrac{m-n_1}{n_0}\)。依据题意,\(len_0\)\(len_1\) 必须为正整数,那么当 \(m-n_0 \cdot len_0\) 不能被 \(n_1\) 整除时 \(len_1\) 就不可能是整数,可以直接跳过这一种情况接着往后枚举。

\(len_0\)\(len_1\) 均为整数时,我们需要得到 \(h_0\)\(h_1\)。如何得到 \(h_0\)\(h_1\) 呢?我们定义变量 \(peroid\) 代表 \(s\) 从第一个字符开始有多少个连续的字符\(s_1\) 相同,比如对字符串“\(\texttt{00010}\)”,\(peroid=3\);对字符串“\(\texttt{1011}\)”,\(peroid=1\)。求出 \(peroid\) 之后,我们开始枚举 \(r_0\)\(r_1\)

\(s\) 的第一个字符为 \(\tt 0\),那么 \(r_0\) 的第一个字符就是 \(t_1\),最后一个字符就是 \(t_{len_0}\)\(h_0\) 的值即为 \(t\) 从第一个字符到第 \(len_0\) 个字符所组成的子串的哈希值。那么 \(h_1\) 呢?这里就需要用到我们刚才求出来的 \(peroid\)\(t\) 从第一个字符算起,有 \(peroid\) 个连续的 \(r_0\),每个 \(r_0\) 的长度为 \(len_0\),那么 \(t\) 中第一个 \(r_1\) 的第一个字符就是 \(t_{len_0 \times peroid+1}\),最后一个字符就 \(t_{len_0 \times peroid+len_1}\)\(h_1\) 的值也就是上述子串的哈希值。

\(s\) 的第一个字符为 \(\tt 1\),过程与上述类似,这里不再赘述。

最后是检查阶段。我们逐个检查 \(s\) 中的字符是否对应 \(t\) 中的相应子串。维护一个指针 \(pos\) ,指向当前所检测子串的第一个字符。若当前子串为 \(r_0\),比较 \(t_{pos}\)\(t_{pos+len_0-1}\) 的哈希值与 \(h_0\);若当前子串为 \(r_1\),比较 \(t_{pos}\)\(t_{pos+len_1-1}\) 的哈希值与 \(h_1\),若二者相等,接着往后比较,反之终止检查,继续往后枚举。

AC Code:

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1e6 + 5;
const int BASE = 13331, MOD = 1e9 + 7;
int n, m, n0 = 0, n1 = 0;
long long Hash[MAXN], Po[MAXN];//十年OI一场空,不开long long见祖宗
char s[MAXN], t[MAXN];
long long get_hash(int l, int r) // 获取子串哈希值
{
    if (l < 1 || r > m) return -1; // 边界检查
    return ((Hash[r] - Hash[l-1] * Po[r-l+1] % MOD) + MOD) % MOD;
}
int main() 
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cin >> (s + 1);
    cin >> (t + 1);
    n = strlen(s + 1);
    m = strlen(t + 1);
    // 预处理哈希值和幂
    Po[0] = 1;
    for (int i = 1; i <= m; i++) 
    {
        Hash[i] = (Hash[i-1] * BASE + (t[i] - 'a' + 1)) % MOD;
        Po[i] = Po[i-1] * BASE % MOD;
    }
    // 统计0和1的数量
    for (int i = 1; i <= n; i++) 
    {
        if (s[i] == '0') n0++;
        else n1++;
    }
    int period = 1;
    // 计算连续相同字符的数量
    for (int i = 2; i <= n; i++) 
    {
        if (s[i] == s[1]) period++;
        else break;
    }
    int ans = 0;
    // 枚举0对应的字符串长度
    for (int len0 = 1; len0 * n0 < m; len0++) 
    {
        if ((m - len0 * n0) % n1 != 0) continue;
        int len1 = (m - len0 * n0) / n1;
        if (len1 <= 0) continue;
        long long h0, h1;
        bool valid = true;
        // 根据首字符类型确定h0和h1
        if (s[1] == '0') 
        {
            if (1 + len0 - 1 > m) continue;
            h0 = get_hash(1, len0);
            int start = 1 + period * len0;
            int end = start + len1 - 1;
            if (end > m) continue;
            h1 = get_hash(start, end);
        } else 
        {
            if (1 + len1 - 1 > m) continue;
            h1 = get_hash(1, len1);
            int start = 1 + period * len1;
            int end = start + len0 - 1;
            if (end > m) continue;
            h0 = get_hash(start, end);
        }        
        if (h0 == h1) continue;// 依题意,两个字符串必须不同
        // 验证整个字符串
        int pos = 1;
        for (int i = 1; i <= n && valid; i++) 
        {
            if (s[i] == '0') 
            {
                if (pos + len0 - 1 > m) 
                {
                    valid = false;
                    break;
                }
                if (get_hash(pos, pos + len0 - 1) != h0) 
                {
                    valid = false;
                    break;
                }
                pos += len0;
            } else 
            {
                if (pos + len1 - 1 > m) 
                {
                    valid = false;
                    break;
                }
                if (get_hash(pos, pos + len1 - 1) != h1) 
                {
                    valid = false;
                    break;
                }
                pos += len1;
            }
        }
        if (valid && pos == m + 1)ans++;           
    }
    cout << ans << endl;
    return 0;
}

完结撒花

posted on 2025-08-11 23:37  望觀  阅读(9)  评论(1)    收藏  举报