CodeForces 535D (2020.3.5训练G题)

题目地址:http://codeforces.com/contest/535/problem/D
CodeForces 535D (2020.3.5训练G题)_第1张图片

题意:题目理解了好久,简化后的大意是,给定原字符串的长度N,子串匹配次数,该子串s,以及可匹配的位置y1,y2…yi,最后要求输出原字符串的所有可能的情况数之和,如果不存在就打印0

刚拿到这题时,没用kmp,单纯就对题意进行模拟,对字符串的每个字符定义一个状态数组,有字符为1,没有为0,每次匹配不断更新数组,N进行减的操作,最后表示空位的个数,答案即为Qpow(26,N,1e9+7),t了三发(=.=),看了网上别的博主写的题解orz,思路清晰多了,现在提供题解和分析

对所给的子串进行求next数组操作(敲板子),重点是e[M]数组,e[i]表示重叠i个时的匹配与否,匹配为1,否为0,注意该循环从M开始逆向进行利用next数组赋值(M==0要特判)

for(int p=m;p;p=nxt[p])
{
	e[p]=1;
}

next数组和e数组赋值结束后,题目就结束一大半了,在ans函数只需要对每两个匹配点求个距离dis,如果大于M,则无需判断重叠,直接N-M,小于等于则要判断,匹配就减dis,不匹配return0(我的pos是从0到M-1,pos[M]要加个等于N+1,方便逆序循环求第一个dis)

完整代码如下:

#include
using namespace std;

#define ll long long
#define mod 1000000007
const int siz=1000005;
bool e[siz];
int N,M,m,i;
char s[siz];
int pos[siz];
int nxt[siz];

ll Qpow(ll a,ll b,ll p)
{
    ll ans=1;
    while(b>0)
    {
        if(b&1) ans=(ans*a)%p;
        a=(a*a)%p;
        b>>=1;
    }
    return ans%p;
}

void getnext()
{
	int j=0;nxt[1]=0;
	for(i=2;i<=m;++i)
	{
		while(j&&s[j+1]!=s[i])j=nxt[j];
		if(s[j+1]==s[i])++j;
		nxt[i]=j;
	}

	for(int p=m;p;p=nxt[p])
	{
		e[p]=1;
	}
}


int ans()
{
	int laplen,dis;
	for(i=M-1;i>=0;--i)
	{
		dis=pos[i+1]-pos[i];
		if(dis>=m)
			N-=m;
		else
		{
			laplen=m-dis;
			if(e[laplen]==0)
				return 0;
			else N-=dis;
		}
	}
	
	return Qpow(26,N,mod);
}

int main()
{
	int i;
	cin>>N>>M;
	scanf("%s",s+1);
	m=strlen(s+1);
	
	for(i=0;i<M;i++)
	{
		cin>>pos[i];
	}
	pos[M]=N+1;
	
	if(M==0)
		cout<<Qpow(26,N,mod)<<endl;
	else 
	{
		getnext();
		cout<<ans()<<endl;
	}
	
}

第一次写题解博客有些叨2333,大佬路过勿忘指点= =

你可能感兴趣的:(CodeForces 535D (2020.3.5训练G题))