Count the string(kmp+dp)

本文介绍了一种使用KMP算法结合动态规划方法解决字符串匹配问题的技术。该方法能够高效计算一个字符串的所有非空前缀在自身出现的次数总和,并通过模运算处理可能的大数值结果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Count the string

It is well known that AekdyCoin is good at string problems as well as number theory problems. When given a string s, we can write down all the non-empty prefixes of this string. For example:
s: "abab"
The prefixes are: "a", "ab", "aba", "abab"
For each prefix, we can count the times it matches in s. So we can see that prefix "a" matches twice, "ab" matches twice too, "aba" matches once, and "abab" matches once. Now you are asked to calculate the sum of the match times for all the prefixes. For "abab", it is 2 + 2 + 1 + 1 = 6.
The answer may be very large, so output the answer mod 10007.

Input
The first line is a single integer T, indicating the number of test cases.
For each case, the first line is an integer n (1 <= n <= 200000), which is the length of string s. A line follows giving the string s. The characters in the strings are all lower-case letters.
Output
For each case, output only one number: the sum of the match times for all the prefixes of s mod 10007.
Sample Input
1
4
abab
Sample Output
6

如果求出next数组以每个子串再用kmp分别求个数肯定会超时。

这个题利用next数组的性质,利用dp,可以得到递推式dp[i] = dp[Next[i]] + 1求,下面具体解释这个递推式的含义和成立原因

dp[i]表示的是以下标为i-1的字母为结尾的任意前缀子串的个数,为什么是i-1,因为字符串从0开始记录,next数组next[i]记录的字符串长度是i实际是下标从0~i-1,同理dp[i]也是存在长度为i的前缀中也就是下标从0~i-1,以i-1号字符为结尾的任意前缀个数

这个公式为什么是对的呢

首先+1就是加上目前这个前缀字符串本身,因为我们又增加了一个字符嘛,比如a b c d  dp[3]存abc中以c结尾任意前缀长度

                                                                                                                        0 1 2 3

+1的目的就是先把abc这个本身加上

然后再来看dp[Next[i]],Next[i]是0~i-1子串s中公共前后缀串的最大长度,同时也是这个子串公共前缀的下一个下标p,

所以dp[p]就代表了我们之前已经求完的,s中最长公共前缀(也是后缀)中以前缀最后一个字符为结尾的任意前缀个数(有点绕),同理他也是后缀的,那么后缀是不是和我们新加的i-1号字符相连,那么之前有多少个,再加上这个新字符是不是就有多少个新的字符了,然后再加1本身,就是以i-1号字符为结尾所有了前缀了

例如   0  1   2  3  4

          a   b  a  b

next  -1   0  0  1  2

dp      0   1  1  2  2

dp[1]=1->"a"   dp[2]=1->"ab"  dp[3]=2->"aba","a"  dp[3]=2->"abab","ab"

code:

#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
const int MAXN = 200100;
const int MOD = 10007;
char s[MAXN];
int n;
int Next[MAXN];
int dp[MAXN];
int sum;
void getNext(){
    int i = -1,j = 0;
    Next[0] = -1;
    int len = strlen(s);
    while(j < len){
        if(i == -1 || s[i] == s[j]){
            i++,j++;
            Next[j] = i;
        }
        else
            i = Next[i];
    }
}

int main(){
    int t;
    scanf("%d",&t);
    while(t--){
      scanf("%d",&n);
      scanf("%s",s);
      getNext();
      int i;
      memset(dp,0,sizeof(dp));
      sum = 0;
      for(i = 1; i <= n; i++){
         dp[i] = dp[Next[i]] + 1;
         sum = (sum + dp[i])%MOD;
      }
      printf("%d\n",sum);
    }
    return 0;
}


### 蓝桥杯 A组 题目解析与答案 #### 试题概述 蓝桥杯作为一项全国性的编程竞赛,其题目设计注重算法思维和实际应用能力。对于A组的题目而言,难度较高,涉及的知识点广泛,涵盖了数据结构、动态规划、贪心算法等多个领域。 以下是基于已有资料整理的部分蓝桥杯A组题目及其解析: --- #### **试题 A: 奇偶覆盖** 该题的核心在于如何通过奇偶性质来判断某些特定条件下的覆盖情况[^2]。 解决此问题的关键是对输入数组进行分类处理,并利用集合运算验证是否存在满足条件的情况。 ```java import java.util.*; public class OddEvenCover { public static void main(String[] args) { Scanner sc = new Scanner(System.in); int n = sc.nextInt(); List<Integer> oddList = new ArrayList<>(); List<Integer> evenList = new ArrayList<>(); for (int i = 0; i < n; i++) { int num = sc.nextInt(); if (num % 2 == 0) { evenList.add(num); } else { oddList.add(num); } } System.out.println("Odd Count: " + oddList.size()); System.out.println("Even Count: " + evenList.size()); // 判断是否可以完全覆盖 boolean canCover = checkCoverage(oddList, evenList); System.out.println(canCover ? "Yes" : "No"); } private static boolean checkCoverage(List<Integer> odds, List<Integer> evens) { Set<Integer> set = new HashSet<>(odds); for (Integer e : evens) { if (!set.contains(e)) { return false; } } return true; } } ``` 上述代码实现了对奇偶数列表的分离以及覆盖率检测功能。 --- #### **试题 B: 补给** 这是一道典型的背包问题变种,主要考察动态规划的应用场景。目标是在有限资源下最大化收益或者完成某种任务的可能性。 解决方案如下所示: ```java public class SupplyProblem { public static final int MAX_WEIGHT = 100; public static void main(String[] args) { int[][] items = { {10, 60}, {20, 100}, {30, 120} }; // 物品重量和价值 int capacity = 50; // 背包容量 int result = knapsack(items, capacity); System.out.println("Maximum Value: " + result); } private static int knapsack(int[][] items, int W) { int n = items.length; int[] dp = new int[W + 1]; for (int i = 0; i < n; i++) { for (int w = W; w >= items[i][0]; w--) { dp[w] = Math.max(dp[w], dp[w - items[i][0]] + items[i][1]); } } return dp[W]; } } ``` 这段程序展示了经典的动态规划方法用于求解最大值问题。 --- #### **试题 C: 玩具蛇** 本题属于字符串匹配类问题,重点是如何高效地查找子串并统计次数。可以通过KMP算法或其他高级模式匹配技术实现更优性能。 下面是简单的暴力法实现版本: ```java public class ToySnake { public static int countOccurrences(String text, String pattern) { int count = 0; int index = 0; while ((index = text.indexOf(pattern, index)) != -1) { count++; index += pattern.length(); // 移动到下一个可能位置 } return count; } public static void main(String[] args) { String str = "abcdeabfghijklmnopqrstuvwxyz"; String pat = "ab"; System.out.println(countOccurrences(str, pat)); // 输出结果应为2 } } ``` 以上代码片段提供了基本的功能框架。 --- #### 总结 通过对这些典型例题的学习可以看出,掌握扎实的基础理论知识加上灵活运用各种技巧是成功解答蓝桥杯A组题目的关键所在。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值