UVA1328
简单题。
我们有结论:对于一个周期串 \(S\) 的子串 \(T\),它的最小循环节即为 \(T-nxt_{\left| T \right|}\)。(具体请查阅往期笔记)
于是,我们枚举所有前缀,检验上式是否能被当前前缀的长度整除并且不止一个循环节即可。
code
#include<bits/stdc++.h>
using namespace std;
const int N=1e7+5;
int nxt[N];
void getnxt(string s){
int i=0,j=-1;
nxt[0]=-1;
for(;i<s.size();){
if(j==-1||s[i]==s[j])
i++,j++,nxt[i]=j;
else
j=nxt[j];
}
}
int main(){
ios::sync_with_stdio(0);
//cin>>t;
int n,p=0; string s;
while(cin>>n&&n){
cin>>s;
getnxt(s);
cout<<"Test case #"<<(++p)<<'\n';
for(int i=2;i<=n;i++)
if(i%(i-nxt[i])==0&&i/(i-nxt[i])>1)
cout<<i<<' '<<i/(i-nxt[i])<<'\n';
cout<<'\n';
}
return 0;
}
P4591
dp 策略请查阅往期笔记。
我们以前做时,使用的 hash 检验是否匹配,而现在仅需在 kmp 中每当匹配成功就转移即可(kmp + dp 一般在 kmp 中转移)。
code
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int K=1e2+5,A=11,S=1e4+5,MOD=1e9+7;
int n,k,ans;
int dp[K][S],a[K];
string s,t[K][A];
int nxt[S];
void getnxt(string t){
int i=0,j=-1;
nxt[0]=-1;
for(;i<t.size();){
if(j==-1||t[i]==t[j])
i++,j++,nxt[i]=j;
else
j=nxt[j];
}
}
void kmp(string s,string t,int cur){
getnxt(t);
int i=0,j=0;
for(;i<s.size();){
if(j==t.size()-1&&s[i]==t[j]){
if(i>=t.size())
dp[cur][i]=(dp[cur][i]+dp[cur-1][i-t.size()])%MOD;
j=nxt[j];
}
if(j==-1||s[i]==t[j])
i++,j++;
else
j=nxt[j];
}
//return 0;
}
signed main(){
cin>>k>>s,n=s.size(),s="#"+s;
for(int i=1;i<=k;i++){
cin>>a[i];
for(int j=1;j<=a[i];j++)
cin>>t[i][j];
}
for(int i=0;i<n;i++) dp[0][i]=1;
for(int i=1;i<=k;i++){
for(int u=1;u<=a[i];u++){
kmp(s,t[i][u],i);
}
}
for(int i=1;i<=n;i++) ans=(ans+dp[k][i])%MOD;
cout<<ans;
return 0;
}
P1470
我们发现实际上我们不关心选到集合 \(O\) 中的第几个元素了,我们仅仅关心当前前缀是哪个。
于是考虑这样定义状态:令 \(dp_i\) 表示以 \(i\) 结尾的前缀能 / 不能由 \(O\) 中元素拼接而成。
初始:\(dp_0=1\)。
答案:最大的满足 \(i \in [1,n]\) 且 \(dp_i=1\) 的 \(i\)。
显然,一个 \(O\) 中元素能拼接成当前前缀,必要条件是当前元素为 \(s\) 的子串。
于是对于每个 \(O\) 中元素将其与 \(s\) 进行 kmp 匹配,记录匹配成功的位置 \(i\) 所对应的元素下标 \(j\)。遍历前缀时,从去除当前前缀的末尾对应的元素的前面部分转移即可。
code
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=2e6+5;
const int MOD=1e9+7;
int n,now,tot,ans;
bool dp[N];
int nxt[N];
string s,p[N];
vector<int> a[N];
void getnxt(string t){
int i=0,j=-1;
nxt[0]=-1;
for(;i<t.size();){
if(j==-1||t[i]==t[j])
i++,j++,nxt[i]=j;
else
j=nxt[j];
}
}
void kmp(string s,string t){
getnxt(t);
int i=0,j=0;
for(;i<s.size();){
if(j==t.size()-1&&s[i]==t[j]){
a[i].push_back(now);
j=nxt[j];
}
if(j==-1||s[i]==t[j])
i++,j++;
else
j=nxt[j];
}
}
signed main(){
while(1){
cin>>p[++tot];
if(p[tot]==".") { tot--; break; }
}
string t;
while(cin>>t) s+=t;
n=s.size(),s="#"+s;
dp[0]=1;
for(int i=1;i<=tot;i++) now=i,kmp(s,p[i]);
for(int i=1;i<=n;i++)
for(int j:a[i])
dp[i]|=dp[i-p[j].size()];
for(int i=n;i>=0;i--)
if(dp[i]) cout<<i,exit(0);
return 0;
}
HDU 1238
枚举第一个串的每个子串,分别用其正序 / 逆序对其他串跑 kmp,若全都成功则取最长即可。
code
#include<bits/stdc++.h>
using namespace std;
const int N=1e2+5;
int tt,n;
string a[N];
int nxt[N];
void getnxt(string t){
int i=0,j=-1;
nxt[0]=-1;
for(;i<t.size();){
if(j==-1||t[i]==t[j])
i++,j++,nxt[i]=j;
else
j=nxt[j];
}
}
bool kmp(string s,string t){
getnxt(t);
int i=0,j=0;
for(;i<s.size();){
if(j==t.size()-1&&s[i]==t[j])
return 1;
if(j==-1||s[i]==t[j])
i++,j++;
else
j=nxt[j];
}
return 0;
}
int main(){
ios::sync_with_stdio(0);
cin>>tt;
while(tt--){
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
int ans=0;
for(int i=0;i<a[1].size();i++){
for(int j=i;j<a[1].size();j++){
string now=a[1].substr(i,j-i+1);
//cout<<now<<'\n';
//string now="";
string tmp=now;
reverse(now.begin(),now.end());
string rnow=now;
now=tmp;
//cout<<now<<' '<<rnow<<'\n';
bool f=1;
for(int k=2;k<=n;k++){
//cout<<a[k]<<'\n';
bool cur1=kmp(a[k],now);
bool cur2=kmp(a[k],rnow);
if(cur1==0&&cur2==0){ f=0; break; }
}
if(f) ans=max(ans,(int)now.size());
}
}
cout<<ans<<'\n';
}
return 0;
}