CSAcademy 접두사 접미사 계수 题 解
문제의 의미
당신이 두 숫자를 부여 \ (N \) 와 \ (M \) 지금, \ (K \) 대표 (M \) \ 자리. 당신을 물어 \ (1 \)가 에 \ (N \) , 숫자 대회의 수 \ (M \) 동시에 그 이전에 \ (K \) 번호와 \ (K는 \) 자리.
사고
우리는 지금 가정 \ (N \) 와 \ (M을 \) 특별히 언급하지 않은 경우 문자열로 취급됩니다, 문자열입니다.
디지털 가정 \ (S \) 이상인 \ (1 \) 이하 \ (N \) , 우리는 그 상태가 분류 될 수 :( 동일한 논의 \ (S \) 문자열 등)를
- \ (S는 \) 길이보다 같 (\ 2K) \ , 그 접두사 및 접미사 도면의 중간 공간을 서로 Motian 부를되어 영향을주지 값을 만족 이하 \ (N \) 바로 충전의 경우 .
- \ (S \) 길이 미만입니다 (2K \) \ , 다음 접두사와 접미사 중복 서로를, 당신은 그들이 접미사되기 전에 충족에 여전히있는, 서로 충돌하지 않는 것을 확인해야합니다 \ (M \) . 그리고, 당신은 채워 가고 디지털 무료이 없습니다.
연습
상술 한 경우에, 개별적으로 구현 될 수있다 :
- 총 길이보다 작은 경우 우리는 현재의 / 숫자의 문자열 길이를 열거 \ (N \) 전체 길이, 그것보다 작아야합니다 \ (N \) 중간 공간을 쉽게 채울 수있을 때. 총 길이가 동일한 경우 (N \) \ 길이, 우리는 이전에이 한 비트를 열거하는 모든 숫자이고 (\ N) \ 보다 디지털 인 동일 (\ N \) 이 작은의 후 바로 다시 채 웁니다. 특히, 이전 가능성이있다 \ (K는 \) 그래서 모든 숫자의 중간에, 과거에 숫자를 표시 쉽게 가득, 또는 모든 숫자의 중간이 가득 (\ 0) \ 현재의 모습이 아직도보다, \ (N \) 큰 후 합법적 인 프로그램이 없다.
- 마찬가지로, 중복 부분의 현재 열거 문자열 길이 / 번호가 빠른 결정을 필요로하는 프리픽스 및 서 픽스와 동일한 조건의 만족 사용될 수
Z
또는KMP
문자열과 일치하는 다른 고속 방법.
프로그램
와 연습 , Z 일치하는 접미사를 사용하기 전에.
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod=1000000007;
char s[1000005];//s表示题目中的数字N
char t[1000005];//t表示题目中的数字M
int n,m;//n, m分别表示题目中的N, M的长度
ll pw[1000005],ans;//pw[i]表示10的i次方对mod取模后的大小,ans表示最终答案
bool check(int emp){//check表示中间空出了emp个填0的数位的时候,前后缀是M的数字是否小于等于N,emp<0时,表示前后缀的M重叠了几个位置
char *r=new char[n+1];
memset(r,'0',n+1);
memcpy(r+1+n-(m+emp+m),t+1,m);
memcpy(r+1+n-m,t+1,m);//构造一个前后缀为M的数字,长度小于N时高位补零
bool fl=false,va=true;
for(int i=1;i<=n;i++){
if(r[i]<s[i])fl=true;
if(r[i]>s[i]&&!fl)va=false;
}//判断大小关系(数学意义)
delete[] r;
return va;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
pw[0]=1;
for(int i=1;i<=1000000;i++)pw[i]=pw[i-1]*10%mod;//预处理
cin>>s+1;
n=strlen(s+1);
cin>>t+1;
m=strlen(t+1);
if(check(-m))ans++;//单个M作为数字,这里其实可能会出锅,但数据中没有
int *z=new int[m+1],l=1,r=1;
z[1]=m;
for(int i=2;i<=m;i++){
z[i]=0;
if(i>r){
l=i;r=i-1;
while(r<m&&t[r+1]==t[z[i]+1])r++,z[i]++;
}else{
if(i+z[i-l+1]<=r){
z[i]=z[i-l+1];
}else{
l=i;
z[i]=r-i+1;
while(r<m&&t[r+1]==t[z[i]+1])r++,z[i]++;
}
}
if(i+z[i]-1==m){
if(m+m-z[i]<n||(m+m-z[i]==n&&check(-z[i])))ans++;
}
}
delete[] z;//Z算法
if(m+m<n||(m+m==n&&check(0)))ans++;//计算有数字形如M+M时是否合法
for(int i=1;m+i+m<n;i++){//枚举空出了i个数字时
ans=(ans+pw[i])%mod;
}
if(m+m<n&&check(n-m-m)){
int emp=n-m-m;
bool pref=false,suff=true,suf=false;//pref表示前K个数字是否已经比N小了,这样可以随便填,suff表示M是否小于等于N的K个数字,是就可以让中间的数字全部等于N对应数位
for(int i=1;i<=m;i++){
if(s[i]>t[i])pref=true;
if(s[i+n-m]>t[i])suf=true;
if(s[i+n-m]<t[i]&&!suf)suff=false;
}
if(pref)ans=(ans+pw[emp])%mod;//加上中间任意填的方案
else{
ans=(ans+suff)%mod;//加上suff的方案
for(int i=m+1;i<=n-m;i++){
ans=(ans+pw[n-m-i]*(s[i]-'0'))%mod;//加上当前位前面的数字和N相同,当前位小于N对应数位,后面随便填的方案
}
}
}
cout<<ans%mod<<endl;
return 0;
}