首先是重点题G题:
反思:1.G题没做出来的直接原因是没看懂题,
2.想尝试用hash做但是没成功//是我太菜了。
题目大意:求s中与m匹配之后还能空缺的可以进行填补的个数
比如说第一个样例:如图所示
刚开始思路就是看两个串之间有没有相交的部分
分类如下:1.有相交部分,若有,如果是前缀则没有空位,如果不是则输出0.
2.若没有相交部分,则需要求得空出部分的个数
第二种情况很好去判断
但是第一种情况就需要考虑到前缀相等的问题,可以使用hash做,最简单的还是使用字符串中的next数组
具体操作步骤就是先找出此字符串中的重复前缀(计为k) 再比较k与length-(a[i]-a[i-1])是否是k的倍数即可
#include<bits/stdc++.h>
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<<next1[k]<<endl;
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<bits/stdc++.h>
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<<next1[k]<<endl;
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<bits/stdc++.h>
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"<<i<<endl;
}
}
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<<next1[k]<<endl;
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.youkuaiyun.com/Binary_Heap/article/details/81983606?depth_1-utm_source=distribute.pc_relevant.none-task&utm_source=distribute.pc_relevant.none-task
这是一个大神写的可以做参考