cf494b kmp&&dp

该博客讨论了一种计算给定字符串s中包含特定子串t的非重叠子串的数量的方法。通过使用KMP算法标记匹配位置,并利用动态规划求解问题,博主给出了详细的过程和解释。

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

http://www.elijahqi.win/archives/487
B. Obsessive String
time limit per test
2 seconds

memory limit per test
256 megabytes

input
standard input

output
standard output

Hamed has recently found a string t and suddenly became quite fond of it. He spent several days trying to find all occurrences of t in other strings he had. Finally he became tired and started thinking about the following problem. Given a string s how many ways are there to extract k ≥ 1 non-overlapping substrings from it such that each of them contains string t as a substring? More formally, you need to calculate the number of ways to choose two sequences a1, a2, …, ak and b1, b2, …, bk satisfying the following requirements:

k ≥ 1

t is a substring of string saisai + 1… sbi (string s is considered as 1-indexed).
As the number of ways can be rather large print it modulo 109 + 7.

Input
Input consists of two lines containing strings s and t (1 ≤ |s|, |t| ≤ 105). Each string consists of lowercase Latin letters.

Output
Print the answer in a single line.

Examples
Input
ababa
aba
Output
5
Input
welcometoroundtwohundredandeightytwo
d
Output
274201
Input
ddd
d
Output
12
这题今晚写的有点神志不清了,明天还得搭乘航班飞往胡志明,先留坑,明早填坑的。

满足以下条件

1、集合中的所有串都是s的子串,且互不重叠 2、集合中的所有串都含有子串t。

设串s长度为n,串t长度为m,我们首先用kmp在s中匹配t,匹配成功的位置我们打下标记flag[i]=1(s[i-m+1…i]=t[1..m]).

设f[i]表示在s中以i结尾的集合数目,sum1[i]为fp[1..i]得前缀和,sum2[i]为sum1[1..i]得前缀和。如果flag[i]为0,则f[i]=f[i-1]。如果flag[i]为1,则dp[i]=i-m+1+sum2[i-m]。证明:若flag[i]=1,说明s[i-m+1…i]=t[1..m],那么sj..i都含有子串t,如果集合中只有一个串的话,一共有i-m+1种,如果在集合中有两个以上匹配的,那么假设我们选择其中一个串,能和他匹配的串,因为不能重叠,所以是f[1]+f[2]+..+f[j-1]即sum1[j-1],那么所有的可能就是sum1[1]+sum1[2]…+sum1[i-m],即sum2[i-m]。所以f[i]=i-m+1+sum2[i-m]

解释一下这个推导过程,大概就是多个完全匹配的时候,那么当前这个,同若只有一个的时候i-m+1

然后前面每个完全匹配的就由sum2提供,在纸上画一下 假设一个s串,然后t在其中有三个匹配的,然后就可以明白了

http://blog.youkuaiyun.com/icefox_zhx/article/details/76038942

#include<cstdio>
#include<cstring>
#define mod 1000000007
#define N 100100
bool flag[N];
int f[N],sum1[N],sum2[N],next[N];
char s[N],t[N];
long long ans;
int main(){
    freopen("cf494b.in","r",stdin);
//  freopen("cf494b.out","w",stdout);
    scanf("%s%s",s+1,t);
    int i=0,j=-1,n=strlen(t);next[0]=-1;
    while (i<n){
        if (j==-1||t[i]==t[j]){
            ++i;++j;next[i]=j;
        }else j=next[j];
    }
    for (int i=n;i>=1;--i) t[i]=t[i-1];
    int m=strlen(s+1);int k=0;
    for (int i=1;i<=m;++i){
        while (k&&s[i]!=t[k+1]) k=next[k];
        if (t[k+1]==s[i])++k;
        if (k==n) flag[i]=true;
    }
    for (int i=1;i<=m;++i){
        if (flag[i]){
            (f[i]=i-n+1+sum2[i-n])%=mod;
        }else f[i]=f[i-1];
        sum1[i]=(sum1[i-1]+f[i])%mod;
        sum2[i]=(sum2[i-1]+sum1[i])%mod;
    }
    for (int i=1;i<=m;++i) (ans+=f[i])%=mod;
    printf("%d",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值