CF - 494B - 字符串预处理 + DP

本文详细解析 CodeForces 494B 的字符串匹配问题,通过预处理和动态规划的方法高效求解,提供完整的 C++ 实现代码。

problem:

http://codeforces.com/problemset/problem/494/B

给你两个字符串。s 和 t , 长度不超过1e5

问有多少1, a2, ..., ak 和 b1, b2, ..., bk 序列对满足以下:

  • k ≥ 1
  •   t is a substring of string saisai + 1... sbi (string s is considered as 1-indexed).
input
ababa
aba
output
5
input
welcometoroundtwohundredandeightytwo
d
output
274201
input
ddd
d
output
12

think:

1. 首先要解读题意。有点抽象。这个看一下样例就好了。

第一个样例:

1,3

1,4

1,5

2,5

3,5
第三个样例:
1,1
1,2
1,3
2,2
2,3
3,3
12,12
12,13
13,13
13,23
23,23
123,123
比如13,23表示 a = 1,3  b = 2,3 

2. 预处理pre数组,表示第i个字符,前面,最靠后的可以和他sub的位置。

3. 前 j 位是一起的。。。然后从 j+1 到 i 组成一个新的(因为他要求 ai > b(i-1))

前j位是dp[j]
从j+1到i是 pri[i] - j 。 因为新加进来的左区间是是 j+1 ~ pri[i]  右区间是 i
然后把这两块乘起来,dp[j] * (pre[i]-j)
然后枚举所有可能的 j  然后展开一下,就O(n)了,
然后加上pri[i], 表示数组只有 1 个,没有 j 
   综上
dp[i] = sum(dp[j] * (pre[i] - j), j < pre[i]) + pre[i] = sum(dp[j]) * pre[i] - sum(dp[j] * j) + pre[i] = sum1[pre[i]-1] * pre[i] - sum2[pre[i] - 1] + pre[i]
sum1[i] = sum[i-1] + dp[i]
sum2[j] = sum[i-1] + dp[i] * i

code:

const int mod = 1000000007;
const int N = 100100;

char a[N];
char b[N];
int pre[N];
int dp[N];
int sum1[N];
int sum2[N];
int fail[N];
int match[N];
int nb, na;

void get_fail(int *fail, const char *p){
    fail[0] = -1;
    for(int i = 1; p[i]; ++i){
        int j = fail[i-1];
        while(j != -1 && p[j+1] != p[i]) j = fail[j];
        fail[i] = (p[i] == p[j+1] ? j + 1 : -1);
    }
}

void kmp(const char *a, const char *b, const int na, const int nb){
    match[0] = (a[0] == b[0] ? 0 : -1);
    if(match[0] == nb - 1) pre[1] = 1;
    else pre[1] = 0;
    for(int i = 1; a[i]; ++i){
        int j = match[i-1];
        if(j == nb - 1) j = fail[j];
        while(j != -1 && a[i] != b[j+1]) j = fail[j];
        match[i] = (a[i] == b[j+1] ? j + 1 : -1);
        if(match[i] == nb - 1) pre[i + 1] = i + 2 - nb;
        else pre[i + 1] = pre[i];
    }
}

int main(){
    scanf("%s%s", a, b);
    int na = strlen(a);
    int nb = strlen(b);
    get_fail(fail, b);
    kmp(a, b, na, nb);
    int ans = 0;
    for(int i = 1; i <= na; ++i) if(pre[i] > 0){
        dp[i] = (LL)sum1[pre[i]-1] * pre[i] % mod;
        dp[i] = (dp[i] - sum2[pre[i]-1] + mod) % mod;
        dp[i] = (dp[i] + pre[i]) % mod;
        ans = (ans + dp[i]) % mod;
        sum1[i] = (sum1[i-1] + dp[i]) % mod;
        sum2[i] = (sum2[i-1] + (LL)dp[i] * i % mod) % mod;
    }
    printf("%d\n", ans);
    return 0;
}


T9-4 Find String 'C' 时间限制:1000ms 内存限制:128MB 文件名:T9-4.in T9-4.out Yuilice拥有着一个神奇的字符串s,该字符串只包含字符'C','O','W',字符串s的长度最高不超过2⋅10 5 。 你可以对于字符串进行以下两种操作: 选择两个相邻相等的字母并将其删除。 选择一个字母,将其替换为另外两个字母的任一排列。 现在Yuilice会给出Q(1≤Q≤2⋅10 5 )个s的子串,请你告诉他,他能否通过以上两种操作将给定的子串变为只包含单个字母'C'的字符串呢? 输入格式 第一行输入一个字符串s 第二行输入一个正整数 Q - 代表Yuilice给出的子串数量 以下 Q 行每行包含两个整数 l 和 r(1≤l≤r≤∣s∣,其中 ∣s∣ 表示 s 的长度) - 代表给出子串的在字符串s当中的范围。 输出格式 输出一个长为 Q 的字符串,如果第 i 个子串可以被转变则第 i 个字符为 'Y',否则为 'N'。 样例组 输入#1 复制 COW 6 1 1 1 2 1 3 2 2 2 3 3 3 输出#1 复制 YNNNYN 提示说明 【样例提示】 第1个子串本身已为符合题目条件的字符串,故为'Y'。 第5个子串可通过2步操作变为'C',即。 OW -> CWW -> C 这个样例字符串 COW 的其他子串均不能被转变为 'C'。 【数据范围】 数据 2-4 满足 ∣s∣≤5000 以及 Q≤5000。 数据 5-11 没有额外限制。 1 #include <iostream> 2 #include <fstream> 3 #include <string> 4 ​ 5 int main() { 6 std::string filename = "example.txt"; 7 ​ 8 // 1. 写入文件 (ofstream) 9 { 10 std::ofstream out_file(filename); // 自动打开文件 11 ​ 12 if (!out_file.is_open()) { 13 std::cerr << "无法打开文件用于写入!" << std::endl; 14 return 1; 15 } 16 ​ 17 out_file << "Hello, World!\n"; 18 out_file << "This is written using C++ fstream.\n"; 19 ​ 20 // 文件将在 out_file 离开作用域时自动关闭(析构函数调用) 21 } // ✅ 此处自动调用析构函数,close() 被隐式执行 22 ​ 23 std::cout << "数据已写入文件。\n"; 24 ​ 25 // 2. 读取文件 (ifstream) 26 { 27 std::ifstream in_file(filename); 28 ​ 29 if (!in_file.is_open()) { 30 std::cerr << "无法打开文件用于读取!" << std::endl; 31 return 1; 32 } 33 ​ 34 std::string line; 35 std::cout << "从文件中读取的内容:\n"; 36 while (std::getline(in_file, line)) { 37 std::cout << line << '\n'; 38 } 39 } 40 ​ 41 return 0; 42 } 43 ​ 输 入 输 出 c++ 文件输入输出加fclose
最新发布
10-26
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值