2020年字符串专题训练赛01

首先是重点题G题:
反思:1.G题没做出来的直接原因是没看懂题,
2.想尝试用hash做但是没成功//是我太菜了。

   题目大意:求s中与m匹配之后还能空缺的可以进行填补的个数
   比如说第一个样例:如图所示
   刚开始思路就是看两个串之间有没有相交的部分
   分类如下:1.有相交部分,若有,如果是前缀则没有空位,如果不是则输出0.
                      2.若没有相交部分,则需要求得空出部分的个数
     第二种情况很好去判断
     但是第一种情况就需要考虑到前缀相等的问题,可以使用hash做,最简单的还是使用字符串中的next数组
     具体操作步骤就是先找出此字符串中的重复前缀(计为k) 再比较k与length-(a[i]-a[i-1])是否是k的倍数即可
#include
using namespace std;
int next1[1000];
int a[1000];
int n,m;string s;
void nt()
{
	int i=0;
	int j=-1;
	next1[0]=-1;
	while(i<s.size())
	{
		if(j==-1||s[i]==s[j])
		{
		next1[++i]=++j;
		}
		else
		j=next1[j];
			}
	 
} 

 int main()
 {
 	int n,m;
 	cin>>n>>m;
 	int sum=0;
 	cin>>s;
 	int k=s.size();
 	nt();
 	for(int i=0;i<m;i++)
 	{
 		cin>>a[i];
	 }	
	// cout<
	for(int i=1;i<n;i++)
	{
		if(a[i]-a[i-1]>=k)
		{
			sum+=a[i]-a[i-1]-k;
		}
		else
		{
			if(a[i]-a[i-1]!=k-next1[k])
			return 0;
			else
			continue;
		}
	}
 	
 }

这就是将此题处理之后的代码
然后需要做的就是计算你所的26^sum次方,这就是会用到快速幂算法//记得将以上的代码改为long long

#include
using namespace std;
long long next1[1000010];
long long a[1000010];
long long n,m;string s;
long long mod=1e9+7;
void nt()
{
	long long i=0;
	long long j=-1;
	next1[0]=-1;
	while(i<s.size())
	{
		if(j==-1||s[i]==s[j])
		{
		next1[++i]=++j;
		}
		else
		j=next1[j];
			}
	 
} 
long long quick(long a,long b)
{
    long long ans=1;
    while(b)
    {
        if(b&1) ans=(ans*a)%mod;
        a=(a*a)%mod;
        b>>=1;
    }
    return ans%mod;
}

 int main()
 {
 	long long n,m;
 	cin>>n>>m;
 		long long cnt;
 	long long sum=0;
 	cin>>s;
 	long long k=s.size();
 	if(m==0)
 	{
 	cnt=quick(26,n);
 	cout<<cnt<<endl;
 	return 0;
	 }
 	nt();
 	for(long long i=0;i<m;i++)
 	{
 		cin>>a[i];
	 }	
	// cout<
	for(long long i=1;i<m;i++)
	{
		if(a[i]-a[i-1]>=k)
		{
			sum+=a[i]-a[i-1]-k;
		}
		else
		{
			if(a[i]-a[i-1]!=k-next1[k])
			{
				
				cout<<0<<endl;
				return 0;
			}
			else
			continue;
		}
		
	}
	
	if(a[m-1]+k-1>n)
	{
	
		cout<<"0"<<endl;
		return 0;
	}
	else
	sum+=n-(a[m-1]+k-1);
 cnt=quick(26,sum);
 	cout<<cnt<<endl;
 }
 

ok到了这里,所有的问题基本上都解决了
交一次,wa了???然后就这样卡了一天。。。
在仔细思考过后发现问题还是出在区间重复的问题上
例如ababab这个例子,其最长的节为4 次长为2 然后在
如果以我写的方式即a[i]-a[i-1]!=k-next1[k]
判断是否是在对应位置,是错误的,只要数据造的好,你就会发现这个具有偶然性,以为即使相等也不一定是在循环节的位置上相等
具体可以实现一下baaab这个数据//队友提供给我的。
再看了众多大神的解法之后,我发现,可以使用一个bool类型的数组,对每一个循环节进行标记,在标记过后,你就会发现你只要判断他们重叠的位置是否为循环节就行了。
因此就需要对next数组进行一下更改,添加一个visited数组
对于visited的标记,我们可以使用以下代码

for(int i=s.size()-1;i;i=next1[i])
			visited[i]=true;
			

这样就可以标记出所有的循环节,因此我们在匹配的时候康康他有没有被标记就行了。。

#include
using namespace std;
long long next1[1000100];
long long a[1000100];
long long n,m;string s;
long long mod=1e9+7;
bool visited[1000100];
void nt()
{
	long long i=0;
	long long j=-1;
	next1[0]=-1;
	while(i<s.size())
	{
		if(j==-1||s[i]==s[j])
		{
		next1[++i]=++j;
		}
		else
		j=next1[j];
			}
			for(int i=s.size()-1;i;i=next1[i])
			{
				visited[i]=true;
			//	cout<<"i"<
			}
	 
} 
long long quick(long a,long b)
{
    long long ans=1;
    while(b)
    {
        if(b&1) ans=(ans*a)%mod;
        a=(a*a)%mod;
        b>>=1;
    }
    return ans%mod;
}

 int main()
 {
 	long long n,m;
 	cin>>n>>m;
 		long long cnt;
 	long long sum=0;
 	cin>>s;
 	long long k=s.size();
 	if(m==0)
 	{
 	cnt=quick(26,n);
 	cout<<cnt<<endl;
 	return 0;
	 }
 	nt();
 	for(long long i=0;i<m;i++)
 	{
 		cin>>a[i];
	 }	
	// cout<
	for(long long i=1;i<m;i++)
	{
		if(a[i]-a[i-1]>=k)
		{
			sum+=a[i]-a[i-1]-k;
		}
		else
		{
			if(!visited[a[i-1]+k-a[i]])
			{
				
				cout<<0<<endl;
				return 0;
			}
			else
			continue;
		}
		
	}
	
	if(a[m-1]+k-1>n)
	{
	
	cout<<"0"<<endl;
		return 0;
	}
	else
	sum+=n-(a[m-1]+k-1);
	sum+=a[0]-1;
 cnt=quick(26,sum);
 	cout<<cnt<<endl;
 }

还需要注意的情况有1.m==0
2.第一个值不一定是从1开始的。。
//反正我是有坑必跳,心累。。。
这道题的做法还有一种是字符串hash做法

https://blog.csdn.net/Binary_Heap/article/details/81983606?depth_1-utm_source=distribute.pc_relevant.none-task&utm_source=distribute.pc_relevant.none-task

这是一个大神写的可以做参考

你可能感兴趣的:(acm,算法,字符串)