KMP-CF535D-Tavas and Malekas

KMP-CF535D-Tavas and Malekas

题意:

给 定 模 式 串 s t r , 以 及 m 个 位 置 p o s [ i ] , 1 < = i < = m , 要 求 从 位 置 p o s [ i ] 开 始 的 后 缀 子 串 的 前 缀 要 包 含 s t r , 求 满 足 条 件 的 , 长 度 为 n 的 字 符 串 共 有 多 少 种 可 能 。 给定模式串str,以及m个位置pos[i],1<=i<=m,要求从位置pos[i]开始的后缀子串的前缀要包含str,\\求满足条件的,长度为n的字符串共有多少种可能。 strmpos[i],1<=i<=mpos[i]strn

输入输出:

Input:
The first line contains two integers n and m, the length of s and the length of the subsequence Malekas wrote down (1 ≤ n ≤ 106 and 0 ≤ m ≤ n - |p| + 1).

The second line contains string p (1 ≤ |p| ≤ n).

The next line contains m space separated integers y1, y2, …, ym, Malekas’ subsequence (1 ≤ y1 < y2 < … < ym ≤ n - |p| + 1).

Output:
In a single line print the answer modulo 1000 000 007.

Sample Input:
6 2
ioi
1 3

Sample Output:
26

Sample Input:
5 2
ioi
1 2

Sample Output:
0

Note:
In the first sample test all strings of form “ioioi?” where the question mark replaces arbitrary English letter satisfy.

Here |x| denotes the length of string x.

Please note that it’s possible that there is no such string (answer is 0).

EG:

样 例 1 : 给 定 母 串 长 度 n = 6 , 模 式 串 长 度 m = 3 , 模 式 串 为 i o i , 要 求 从 1 , 3 位 置 开 始 的 后 缀 子 串 的 前 缀 为 i o i , 可 以 确 定 长 度 为 6 的 母 串 的 前 5 位 字 符 为 i o i o i _ , 第 六 位 可 填 任 意 字 母 , 因 此 满 足 题 意 的 数 量 为 2 6 1 = 26 。 样例1:给定母串长度n=6,模式串长度m=3,模式串为ioi,要求从1,3位置开始的后缀子串的前缀为ioi,\\可以确定长度为6的母串的前5位字符为ioioi\_,第六位可填任意字母,因此满足题意的数量为26^1=26。 1n=6m=3ioi1,3ioi65ioioi_261=26

题解:

一 开 始 的 想 法 是 先 计 算 相 邻 位 置 p o s [ i + 1 ] 与 p o s [ i ] 之 差 , 若 差 小 于 模 式 串 长 度 l e n − 1 就 无 法 满 足 条 件 , 若 恰 好 等 于 l e n − 1 , 还 需 要 判 断 模 式 串 的 第 一 个 字 符 和 最 后 一 个 字 符 是 否 相 同 , 如 样 例 2 。 一开始的想法是先计算相邻位置pos[i+1]与pos[i]之差,若差小于模式串长度len-1就无法满足条件,\\若恰好等于len-1,还需要判断模式串的第一个字符和最后一个字符是否相同,如样例2。 pos[i+1]pos[i]len1len12

现 在 发 现 了 思 考 的 漏 洞 : 现在发现了思考的漏洞: :

如 : 模 式 串 a b a b , 长 度 l e n = 4 , 若 两 个 相 邻 位 置 间 隔 为 1 , 1 < 4 − 1 = 3 , 却 仍 然 可 以 满 足 题 意 。 如:模式串abab,长度len=4,若两个相邻位置间隔为1,1<4-1=3,却仍然可以满足题意。 abab,len=411<41=3
KMP-CF535D-Tavas and Malekas_第1张图片
所以,判断的依据应当是模式串最长相同前后缀的长度!

接 下 来 需 要 统 计 相 邻 位 置 之 间 “ 可 以 填 任 意 字 母 ” 的 位 置 的 个 数 , 也 不 要 忘 了 最 后 要 判 断 总 长 度 是 否 超 过 n 。 求 出 总 的 “ 空 位 ” 个 数 c n t , 再 计 算 2 6 c n t 即 最 终 方 案 总 数 。 接下来需要统计相邻位置之间“可以填任意字母”的位置的个数,也不要忘了最后要判断总长度是否超过n。\\求出总的“空位”个数cnt,再计算26^{cnt}即最终方案总数。 ncnt26cnt

用 到 最 长 相 同 前 后 缀 的 长 度 , 想 到 K M P 算 法 中 的 N e x t 数 组 。 用到最长相同前后缀的长度,想到KMP算法中的Next数组。 KMPNext

N e x t [ i ] : 前 i 个 字 符 中 , 前 缀 与 后 缀 相 同 部 分 最 长 的 长 度 。 Next[i]:前i个字符中,前缀与后缀相同部分最长的长度。 Next[i]i

所 以 我 们 对 模 式 串 求 N e x t 数 组 后 , 利 用 N e x t 数 组 从 模 式 串 末 尾 开 始 , 递 归 标 记 每 一 个 循 环 节 的 前 一 个 循 环 节 的 一 端 。 所以我们对模式串求Next数组后,利用Next数组从模式串末尾开始,递归标记每一个循环节的前一个循环节的一端。 NextNext

如 : 对 模 式 串 a b a b , i : 1    2    3    4 N e x t [ i ] :   0    0    1    2 , 我 们 把 从 末 尾 4 开 始 标 记 , 接 着 N e x t [ 4 ] = 2 , 再 把 2 标 记 。 若 模 式 串 长 度 减 去 相 邻 位 置 的 间 隔 恰 好 被 标 记 过 , 即 l e n − ( p o s [ i + 1 ] − p o s [ i ] ) 在 模 式 串 中 的 位 置 恰 好 是 前 一 个 循 环 节 的 一 端 , 说 明 可 行 。 反 应 到 上 图 就 是 4 − 2 = 2 , 恰 好 是 后 一 个 循 环 节 a b 的 前 一 个 循 环 节 a b 的 后 端 所 在 的 位 置 。 如:对模式串abab,\\ \qquad \quad i: 1\ \ 2\ \ 3\ \ 4\\Next[i]: \ 0\ \ 0\ \ 1\ \ 2,\\我们把从末尾4开始标记,接着Next[4]=2,再把2标记。\\若模式串长度减去相邻位置的间隔恰好被标记过,\\即len-(pos[i+1]-pos[i])在模式串中的位置恰好是前一个循环节的一端,说明可行。\\反应到上图就是4-2=2,恰好是后一个循环节ab的前一个循环节ab的后端所在的位置。 ababi1  2  3  4Next[i]: 0  0  1  2,4Next[4]=22len(pos[i+1]pos[i])42=2abab

具体落实:

① 、 利 用 K M P 中 的 N e x t 数 组 来 判 断 , 从 最 后 一 个 循 环 节 向 前 标 记 每 一 个 循 环 节 的 末 端 。 ①、利用KMP中的Next数组来判断,从最后一个循环节向前标记每一个循环节的末端。 KMPNext
② 、 计 算 相 邻 位 置 的 间 隔 , 若 间 隔 长 度 小 于 模 式 串 的 长 度 − 1 , 则 需 要 判 断 是 否 处 在 某 循 环 节 的 末 端 位 置 。 ③ 、 计 算 所 有 “ 空 位 ” 的 数 量 c n t , 总 的 方 案 数 即 2 6 c n t 。 ②、计算相邻位置的间隔,若间隔长度小于模式串的长度-1,则需要判断是否处在某循环节的末端位置。\\③、计算所有“空位”的数量cnt,总的方案数即26^{cnt}。 1cnt26cnt

注意:
① 、 m = 0 的 情 况 不 能 漏 了 特 判 。 ② 、 最 后 一 个 位 置 的 模 式 串 可 能 会 超 过 长 度 n 。 ③ 、 第 一 个 位 置 未 必 是 1 , 因 此 第 一 个 位 置 之 前 仍 然 可 能 存 在 “ 空 位 ” 。 ①、m=0的情况不能漏了特判。\\②、最后一个位置的模式串可能会超过长度n。\\③、第一个位置未必是1,因此第一个位置之前仍然可能存在“空位”。 m=0n1


代码:

#include
#include
#include
#include
#include
#define ll  long long
#define inf 0x7fffffff
using namespace std;
const int mod=1e9+7;
const int N=1e6+10;
int n,m,len;   ///len为模式串的长度
char s[N];
int pos[N],Next[N];
bool mark[N];   ///标记

void get_next()
{
     
    for(int i=2,j=0;i<=len;i++)
    {
     
        while(j&&s[i]!=s[j+1]) j=Next[j];
        if(s[i]==s[j+1]) j++;
        Next[i]=j;
    }
}

ll quick_pow(ll a,ll b)
{
     
    ll ans=1;
    while(b)
    {
     
        if(b&1) ans=(ans*a)%mod;
        a=(a*a)%mod;
        b>>=1;
    }
    return ans%mod;
}

int main()
{
     
    cin>>n>>m;
    scanf("%s",s+1);
    if(!m) {
     printf("%lld\n",quick_pow(26,n));return 0;}   ///不能忘了特判m=0
    len=strlen(s+1);

    get_next();

    for(int i=len;i;i=Next[i]) mark[i]=true;

    for(int i=1;i<=m;i++)
    {
     
        scanf("%d",&pos[i]);
        if(i!=1&&pos[i]-pos[i-1]<len)   ///相邻位置间隔小于模式串长度的情况,需要判断是否被Next数组标记过
        {
     
            if(!mark[ pos[i-1]+len-pos[i] ])
            {
     
                cout<<0<<endl;
                return 0;
            }
        }
    }

    if(n-pos[m]<len-1) cout<<0<<endl;  ///判断最后一个位置是否会超过长度n
    else  ///统计空位的数量
    {
     
        int cnt=pos[1]-1;  ///第一个位置前的数字也是任意的
        for(int i=2;i<=m;i++)
            if(pos[i]-pos[i-1]>len)
                cnt+=pos[i]-pos[i-1]-len;

        cnt+=n-pos[m]-len+1;  ///加上最后一个位置到末尾的空位

        printf("%lld\n",quick_pow(26,cnt));
    }

    return 0;
}

你可能感兴趣的:(字符串,算法,字符串,数据结构,kmp,acm竞赛)