zoukankan      html  css  js  c++  java
  • CF 494B 【Obsessive String】

    很有趣的一道题

    这道题提议很难懂,其实就是让你求合法的集合数目。合法的集合定义为:

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

    看到网上很多题解说要用kmp,但我就不用...

    因为仅需进行一个字符串匹配,而hash是很好写的匹配啊

    而且kmp的next指针在dp中并没有起到作用。

    说一下主体思路吧:

    设两个字符串为s,t,长度分别为l1,l2

    首先我们在原串中查找所有的位置i,使s中以i为结尾的子串与t匹配

    对于所有的位置i,标记flag[i]=1;

    然后我们进行dp

    设dp[i]表示以选取的所有集合中集合的最后一个元素的结尾均为i,开头为j(j不体现在状态中,1<=j<=i-l2+1)的所有方案数

    那么答案就是∑(i=1~l1)dp[i]

    接下来我们考虑转移

    首先,对于某一位置,如果flag[i]=0,我们有:

    dp[i]=dp[i-1]

    原因:如果到这一位置没有匹配上,那么说明这个位置只能被包含在前一个状态中。

    那么,如果flag[i]=1,怎么办?

    我们考虑集合中元素的个数:

    如果只有一个元素,那么由于flag[i]=1,说明i-l2+1~i与t是可以完全匹配的,所以从1到i-l2+1都可以作为这个元素的起点,所以方案数为i-l2+1

    如果有两个以上元素,那么最后一个元素的起点就可以是2~i-l2+1

    那么我们设这个起点是k

    于是上一个元素的终点就可以是1~k-1

    所以如果起点是k,总方案数就是∑(i=1~k-1)dp[i]

    那这个东西就可以用一个前缀和s来维护

    而由于终点可以是2~i-l2+1,所以一共的总方案数就是:

    ∑(i=2-i-l2+1)s[i-1]

    也就是:

    ∑(i=1-i-l2)s[i]

    发现这也是一个前缀和的形式,于是我们把s维护成一个前缀和ss

    结合以上两个分析,得到转移为:

    dp[i]=i-l2+1+ss[i-l2]

    边递推边维护即可。

    #include <cstdio>
    #include <cmath>
    #include <cstring>
    #include <cstdlib>
    #include <iostream>
    #include <algorithm>
    #include <queue>
    #include <stack>
    #define seed 13131
    #define ull unsigned long long
    #define mode 1000000007
    #define ll long long
    using namespace std;
    ll dp[100005];
    ll s1[100005];
    ll s2[100005];
    char s[100005];
    char t[100005];
    ull has,has1[100005];
    ull v;
    bool flag[100005];
    int main()
    {
        scanf("%s%s",s+1,t+1);
        v=1;
        int l1=strlen(s+1),l2=strlen(t+1);
        for(int i=1;i<=l1;i++)
        {
            has1[i]=has1[i-1]*seed+s[i]-'a'+1;
        }
        v=1;
        for(int i=1;i<=l2;i++)
        {
            has=has*seed+t[i]-'a'+1;
            v*=seed;
        }
        for(int i=l2;i<=l1;i++)
        {
            int st=i-l2;
            ull hast=has1[i]-has1[st]*v;
            if(hast==has)
            {
                flag[i]=1;
            }
        }
        for(int i=1;i<=l1;i++)
        {
            if(!flag[i])
            {
                dp[i]=dp[i-1];
            }else
            {
                dp[i]=((i-l2+1)+s2[i-l2])%mode;
            }
            s1[i]=(s1[i-1]+dp[i])%mode;
            s2[i]=(s2[i-1]+s1[i])%mode;
        }
        printf("%lld
    ",s1[l1]);
        return 0;
    }
  • 相关阅读:
    藏!Java编程技巧之单元测试用例编写流程 原创 常意 阿里技术 2021-05-12
    时间,遵循rfc3339标准格式 unix时间戳
    微软面试题: LeetCode 240. 搜索二维矩阵 II 出现次数:3
    微软面试题: LeetCode 69. x 的平方根 出现次数:3
    微软面试题: LeetCode 138. 复制带随机指针的链表 出现次数:3
    微软面试题: LeetCode 384. 打乱数组 出现次数:3
    微软面试题: LeetCode 207. 课程表 出现次数:3
    微软面试题: LeetCode 98. 验证二叉搜索树 出现次数:3
    CF1537E2 Erase and Extend (Hard Version) 题解
    洛谷 P4332 [SHOI2014]三叉神经树 题解
  • 原文地址:https://www.cnblogs.com/zhangleo/p/10764232.html
Copyright © 2011-2022 走看看