题意:给你两个串,问你第二个串是从第一个串的什么位置开始完全匹配的? kmp裸题,复杂度O(n+m)。
当一个字符串以0为起始下标时,next[i]可以描述为"不为自身的最大首尾重复子串长度"。
当发生失配的情况下,j的新值next[j]取决于模式串中T[0 ~ j-1]中前缀和后缀相等部分的长度, 并且next[j]恰好等于这个最大长度。
防止超时,注意一些细节。。
另外:尽量少用strlen,变量记录下来使用比较好,用字符数组而不用string
#include <bits/stdc++.h>
//#pragma GCC optimize(2)
//#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
typedef long long ll;
const int maxn=1e6+5;
const int maxm=1e4+5;
int a[maxn];
int b[maxn];
int Next[maxn];
int n,m;
void init() {
}
void getNext()
{
int k=-1;
int j=0;
Next[j]=k;
while(j<m)
{
if(k==-1||b[k]==b[j])
{
k++;
j++;
Next[j]=k;
}
else
k=Next[k];
}
}
int KMP(int start)
{
int i=start,j=0;
while(i<n&&j<m)
{
if(j==-1||a[i]==b[j])
{
i++,j++;
}
else
j=Next[j];
}
if(j==m)
return i-j+1;
return -1;
}
void solve() {
cin>>n>>m;
for(int i=0;i<n;i++)
{
cin>>a[i];
}
for(int i=0;i<m;i++)
{
cin>>b[i];
}
getNext();
cout<<KMP(0)<<"\n";
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
int _ = 1;
cin>>_;
while (_--) {
init();
solve();
}
return 0;
}
题目翻译:给出一个模式串,给出一个文本串,求模式串在文本串中出现
了多少次?
#include <bits/stdc++.h>
//#pragma GCC optimize(2)
//#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
typedef long long ll;
const int maxn=1e6+5;
const int maxm=1e4+5;
string a,b;
int n,m;
int Next[maxn];
void init() {
}
void getNext()
{
int k=-1;
int j=0;
Next[j]=k;
while(j<m)
{
if(k==-1||b[k]==b[j])
{
k++;
j++;
Next[j]=k;
}
else
k=Next[k];
}
}
int KMP(int start)
{
int ans=0;
int i=start,j=0;
while(i<n)
{
if(j==-1||a[i]==b[j])
{
i++,j++;
}
else
j=Next[j];
if(j==m)
{
ans++;
}
}
return ans;
}
void solve() {
cin>>b>>a;
n=a.size();
m=b.size();
getNext();
cout<<KMP(0)<<"\n";
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
int _ = 1;
cin>>_;
while (_--) {
init();
solve();
}
return 0;
}
题意:输入两个字符串,问第二个字符串在第一个字符串中的出现次数。(不可可重叠)。
#include <bits/stdc++.h>
//#pragma GCC optimize(2)
//#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
typedef long long ll;
const int maxn=1e6+5;
const int maxm=1e4+5;
string a,b;
int n,m;
int Next[maxn];
void init() {
}
void getNext()
{
int k=-1;
int j=0;
Next[j]=k;
while(j<m)
{
if(k==-1||b[k]==b[j])
{
k++;
j++;
Next[j]=k;
}
else
k=Next[k];
}
}
int KMP(int start)
{
int ans=0;
int i=start,j=0;
while(i<n)
{
if(j==-1||a[i]==b[j])
{
i++,j++;
}
else
j=Next[j];
if(j==m)
{
ans++;
j=0;
}
}
return ans;
}
void solve() {
while(cin>>a)
{
if(a[0]=='#')
return;
cin>>b;
n=a.size();
m=b.size();
getNext();
cout<<KMP(0)<<"\n";
}
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
int _ = 1;
//cin>>_;
while (_--) {
init();
solve();
}
return 0;
}
题目大意:
给一个字符串,问再加几个字符能构成循环
题目分析:
我们可以利用next数组的性质 求得串的最小循环元大小,最小循环元大小等于len - next[末尾位置],如果串的长度能够整除循环元,则原串已经循环,不需要补全,否则,循环元大小减去串的长度对最小循环元的余数即为需要补的字符数。
#include <bits/stdc++.h>
//#pragma GCC optimize(2)
//#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
typedef long long ll;
const int maxn=1e6+5;
int Next[maxn];
void init() {
}
int n;
string s;
void getNext()
{
int k=-1;
int j=0;
Next[j]=k;
while(j<n)
{
if(k==-1||s[k]==s[j])
{
k++;
j++;
Next[j]=k;
}
else
k=Next[k];
}
}
void solve() {
cin>>s;
n=s.size();
getNext();
int l=n-Next[n];
if(n%l==0&&n!=l)
cout<<0<<endl;
else
cout<<l-n%l<<endl;
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
int _ = 1;
cin>>_;
while (_--) {
init();
solve();
}
return 0;
}
题意
给一字符串,求其所有完整循环的前缀与循环节的长度。
例:aaa
长度2前缀,循环节为a,个数为2
长度3前缀,循环节为a,个数为3
思路
kmp求出字符串前后缀重复数,遍历所有前缀子串进行下面操作:
字符串前后缀重复数next[L],则循环节的长度为L-L%next[L],如果L%循环节长度为0,则说明是完整循环,输出解。
#include <bits/stdc++.h>
//#pragma GCC optimize(2)
//#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
typedef long long ll;
const int maxn=1e6+5;
int Next[maxn];
void init() {
}
int n;
string s;
void getNext()
{
int k=-1;
int j=0;
Next[j]=k;
while(j<n)
{
if(k==-1||s[k]==s[j])
{
k++;
j++;
Next[j]=k;
}
else
k=Next[k];
}
}
void solve() {
int t=0;
while(cin>>n&&n)
{
t++;
cin>>s;
getNext();
cout<<"Test case #"<<t<<endl;
for(int i=2;i<=n;i++)
{
int l=i-Next[i];
if(i%l==0&&i/l!=1)
{
cout<<i<<" "<<i/l<<endl;
}
}
cout<<endl;
}
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
int _ = 1;
//cin>>_;
while (_--) {
init();
solve();
}
return 0;
}
【题意】
给出一个字符串,要把它写成(x)n的形式,问n的最大值。
【思路】
方法一:后缀数组 (2485MS)
用后缀数组求出字符串的各种信息:Rank数组,height数组等等。
然后我们从1开始枚举循环节的长度i。如果字符串存在长度大于等于2的循环节,需要满足以下条件
首先循环节的长度必须是字符串长度的因子。
要保证Rank[0]==Rank[i]+1,因为两者比较字典序的时候前面的都相同,只是原串更长一些。
而且从i开始的后缀与原串的公共前缀为前者的长度即len-i。
否则结果就为1。
PS: 此题数据范围较大,用DA算法会超时,用DC3算法也只是刚刚过,所以建议用下面的方法。
方法二: KMP (141MS)
设字符串的长度为len,我们知道nex[len]表示既是原串前缀又是原串后缀的字符串最大长度(不包括本身),所以如果字符串的循环节长度大于等于2,那么len-nex[len]一定是字符串的最小周期,且一定能整除len。否则输出1。
//#include <bits/stdc++.h>
#include<iostream>
//#pragma GCC optimize(2)
//#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
typedef long long ll;
const int maxn=1e6+5;
int Next[maxn];
void init() {
}
int n;
string s;
void getNext()
{
int k=-1;
int j=0;
Next[j]=k;
while(j<n)
{
if(k==-1||s[k]==s[j])
{
k++;
j++;
Next[j]=k;
}
else
k=Next[k];
}
}
void solve() {
while(cin>>s)
{
if(s[0]=='.')
return;
n=s.size();
getNext();
int l=n-Next[n];
if(n%l==0)
cout<<n/l<<endl;
else
cout<<1<<endl;
}
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
int _ = 1;
//cin>>_;
while (_--) {
init();
solve();
}
return 0;
}
给定一个字符串,求出所有既是前缀又是后缀的子串。
思路
KMP 的next数组的运用(改进前),如果next数组中next[ i ]= k 正值,那么就代表这从 “0 ~ i-1”位置的子串的前缀后缀相同的最大长度为k,并且k代表着与当前后缀最大匹配的前缀位置。
0 1 2 3 4 5 6 7 8 9 10 11 12
字符串 a a b a a b a a b a a b
next [ ] -1 0 1 0 1 2 3 4 5 6 7 8 9
如表 next[ 12 ] = 9 , 9 所在位置的前缀 为 a a b a a b a a b,与后缀是最大匹配 , next[ 9 ] = 6 ,6 所在位置的前缀 为 a a b a a b,仍然和后缀匹配,第二大匹配
next[ 6 ] = 3 , ,a a b, next[ 3 ] = 0,表明需要回溯到头再进行匹配。
因而通过前后缀重复次数的计数,以及next数组的调转,我们就能尽可能少的重复 重复位置的匹配。每次都能跳转到 前后缀最大公共的位置。
因而对于这个题,我们从最后的next[ len ]开始,不断地找 next[next[i]], 所找的前缀都和最后的后缀所匹配,所在位置的值就是前后缀相同的长度。全部累加就可以了。
另外还要加上满足条件的字符串本身。
//#include <bits/stdc++.h>
#include<iostream>
#include<vector>
//#pragma GCC optimize(2)
//#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
typedef long long ll;
const int maxn=1e6+5;
int Next[maxn];
vector<int>vv;
void init() {
vv.clear();
}
int n;
string s;
void getNext()
{
int k=-1;
int j=0;
Next[j]=k;
while(j<n)
{
if(k==-1||s[k]==s[j])
{
k++;
j++;
Next[j]=k;
}
else
k=Next[k];
}
}
void solve() {
while(cin>>s)
{
init();
n=s.size();
getNext();
vv.push_back(n);
int x=Next[n];
while(x)
{
vv.push_back(x);
x=Next[x];
}
for(int i=vv.size()-1;i>=0;i--)
cout<<vv[i]<<" ";
cout<<endl;
}
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
int _ = 1;
//cin>>_;
while (_--) {
init();
solve();
}
return 0;
}
题目大意:给你N个长度为60的字符串(N<=10),求他们的最长公共子串(长度>=3)。
题目分析:KMP字符串匹配基础题。直接枚举第1个字符串的所有子串,判断这个子串是否出现在另外N-1个串中
//#include <bits/stdc++.h>
#include<iostream>
#include<vector>
#include<algorithm>
//#pragma GCC optimize(2)
//#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
typedef long long ll;
const int maxn=1e6+5;
int Next[maxn];
int m;
string t,p;
vector<string >vv;
vector<string >uu;
void init() {
uu.clear();
vv.clear();
}
bool cmp(string a,string b)
{
if(a.size()==b.size())
{
return a<b;
}
return a.size()>b.size();
}
void getNext()
{
int k=-1;
int j=0;
Next[j]=k;
while(j<m)
{
if(k==-1||t[k]==t[j])
{
k++;
j++;
Next[j]=k;
}
else
k=Next[k];
}
}
bool KMP(int start)
{
int i=start,j=0;
while(i<60&&j<m)
{
if(j==-1||p[i]==t[j])
{
i++,j++;
}
else
j=Next[j];
}
if(j==m)
return true;
return false;
}
void solve() {
string s;
int n;
cin>>n;
for(int i=0;i<n;i++)
{
cin>>s;
uu.push_back(s);
}
for(int i=0;i<60;i++)
{
for(int j=3;i+j<=60;j++)
{
vv.push_back(uu[0].substr(i,j));
}
}
sort(vv.begin(),vv.end(),cmp);
vv.erase(unique(vv.begin(),vv.end()),vv.end());
int flag;
for(int i=0;i<vv.size();i++)
{
t=vv[i];
m=t.size();
getNext();
flag=1;
for(int j=0;j<uu.size();j++)
{
p=uu[j];
if(KMP(0))
{
}
else
{
flag=0;
break;
}
}
if(flag==1)
{
cout<<t<<endl;
return;
}
}
cout<<"no significant commonalities"<<endl;
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
int _ = 1;
cin>>_;
while (_--) {
init();
solve();
}
return 0;
}
题目大意:给定两个字符串,在第一个字符串中找到一个最大前缀作为第二个字符串的后缀
思路:将S1作为模式串 然后在s2中寻找,求出s1的next数组,nxt[i]:x[i…m-1]与x[0…m-1]的最长公共前缀。同时求出s2的extend数组,extend[i]:y[i…n-1]与x[0…m-1]的最长公共前缀
//#include <bits/stdc++.h>
#include<iostream>
#include<vector>
#include<algorithm>
//#pragma GCC optimize(2)
//#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
typedef long long ll;
const int maxn=1e6+5;
int Next[maxn];
int m;
string t,p;
void init() {
}
void getNext()
{
int k=-1;
int j=0;
Next[j]=k;
while(j<m)
{
if(k==-1||t[k]==t[j])
{
k++;
j++;
Next[j]=k;
}
else
k=Next[k];
}
}
bool KMP(int start)
{
int i=start,j=0;
while(i<60&&j<m)
{
if(j==-1||p[i]==t[j])
{
i++,j++;
}
else
j=Next[j];
}
if(j==m)
return true;
return false;
}
void solve() {
string a,b;
while(cin>>a>>b)
{
int min_=min(a.size(),b.size());
t=a+b;
m=t.size();
getNext();
int xx=Next[m];
while(xx>min_)
{
xx=Next[xx];
}
if(xx==0)
{
cout<<0<<endl;
}
else
{
cout<<a.substr(0,xx);
cout<<" "<<xx<<endl;
}
}
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
int _ = 1;
//cin>>_;
while (_--) {
init();
solve();
}
return 0;
}
题目大意:
发送一个密文,为字符串S。这段密文的前半部份是加密过的,后半部分是没有加密过的。现在这段密文被截获,但是密文的尾部的一部份损失了。例如,假设密文是xxxxzzzz, xxxx是加密过的,zzzz是没加密的,因为损失了后面一部份,所以截获的内容可能为xxxxzz, 可以保证截获的内容的无加密内容一定是完整的。
给出密码表,这个密码表为一串26个字母的字符串,a[0]表示a加密后的字母,a[1]表示b加密后的字母…a[25]表示z加密后的字母。
要你恢复到无损坏的密文。在截获的内容中,要让明文最短。
分析与总结:
先来看看暴力的方法是怎样的。直接从S的一半位置开始枚举,然后判断后面部分的经过加密后是否都和明文部分相匹配即可。
事实证明,数据弱了,可以暴力水过。
如果不水,这题的正解应该是用拓展KMP(资料)
先把S全部都当作是密文的,然后把S全转换成明文,保存为T
这时,S中的密文部分就全部都变成了明文,而明文部分都变成了xxx(不用管是什么)。
然后可以发现,原来的S中的明文部分是S的后缀,而T中的明文部分是T的前缀。
所以,演变成了求S【i…n】中的与T的最长公共前缀,就是赤裸的拓展KMP问题了。
//#include <bits/stdc++.h>
#include<iostream>
#include<vector>
#include<algorithm>
//#pragma GCC optimize(2)
//#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
typedef long long ll;
const int maxn=1e6+5;
int Next[maxn];
int m;
string t,p;
void init() {
}
void getNext()
{
int k=-1;
int j=0;
Next[j]=k;
while(j<m)
{
if(k==-1||t[k]==t[j])
{
k++;
j++;
Next[j]=k;
}
else
k=Next[k];
}
}
bool KMP(int start)
{
int i=start,j=0;
while(i<60&&j<m)
{
if(j==-1||p[i]==t[j])
{
i++,j++;
}
else
j=Next[j];
}
if(j==m)
return true;
return false;
}
void solve() {
cin>>p>>t;
string s=t,ss=p;
m=t.size();
for(int i=m/2;i<m;i++)
{
t[i]=p[t[i]-'a'];
}
getNext();
int xx=m;
while(xx>m/2)
{
xx=Next[xx];
}
int n=m-xx;
for(int i=0;i<n;i++)
cout<<s[i];
for(int i=0;i<26;i++)
ss[p[i]-'a']=(char)(i+'a');
for(int i=0;i<n;i++)
cout<<ss[s[i]-'a'];
cout<<endl;
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
int _ = 1;
cin>>_;
while (_--) {
init();
solve();
}
return 0;
}
在给定字符串中找到最长公共子串, 或者给定字符串子串的反串;
最多100字符串,最长100,直接枚举
求反串可以用 中的reverse函数 ;
#include <bits/stdc++.h>
#include<iostream>
#include<vector>
#include<algorithm>
//#pragma GCC optimize(2)
//#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
typedef long long ll;
const int maxn=1e6+5;
int Next[maxn];
int nex[maxn];
int m,mm;
string t,p;
string temp;
vector<string>vv,uu;
void init() {
vv.clear();
uu.clear();
}
bool cmp(string aa,string bb)
{
return aa.size()>bb.size();
}
void getNext()
{
int k=-1;
int j=0;
Next[j]=k;
while(j<m)
{
if(k==-1||t[k]==t[j])
{
k++;
j++;
Next[j]=k;
}
else
k=Next[k];
}
}
void getnex()
{
int k=-1;
int j=0;
nex[j]=k;
while(j<m)
{
if(k==-1||temp[k]==temp[j])
{
k++;
j++;
nex[j]=k;
}
else
k=nex[k];
}
}
bool KMP(int start)
{
int i=start,j=0;
while(i<mm&&j<m)
{
if(j==-1||p[i]==t[j])
{
i++,j++;
}
else
j=Next[j];
}
if(j==m)
return true;
return false;
}
bool kmp(int start)
{
int i=start,j=0;
while(i<mm&&j<m)
{
if(j==-1||p[i]==temp[j])
{
i++,j++;
}
else
j=nex[j];
}
if(j==m)
return true;
return false;
}
void solve() {
int n;
cin>>n;
string mn;
for(int i=0;i<n;i++)
{
string s;
cin>>s;
if(i==0)
mn=s;
vv.push_back(s);
if(s.size()<mn.size())
mn=s;
}
for(int i=0;i<mn.size();i++)
{
for(int j=1;i+j<=mn.size();j++)
{
uu.push_back(mn.substr(i,j));
}
}
sort(uu.begin(),uu.end(),cmp);
for(int i=0;i<uu.size();i++)
{
int flag=1;
t=uu[i];
temp=t;
reverse(temp.begin(),temp.end());
m=t.size();
getnex();
getNext();
for(int j=0;j<vv.size();j++)
{
p=vv[j];
mm=p.size();
if(KMP(0)||kmp(0))
{
}
else
{
flag=0;
break;
}
}
if(flag==1)
{
cout<<m<<endl;
return;
}
}
cout<<0<<endl;
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
int _ = 1;
cin>>_;
while (_--) {
init();
solve();
}
return 0;
}
题目大意:给出若干个串,求最长公共子串。
#include <bits/stdc++.h>
#include<iostream>
#include<vector>
#include<algorithm>
//#pragma GCC optimize(2)
//#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
typedef long long ll;
const int maxn=1e6+5;
int Next[maxn];
int nex[maxn];
int m,mm;
string t,p;
string temp;
vector<string>vv,uu;
void init() {
vv.clear();
uu.clear();
}
bool cmp(string aa,string bb)
{
if(aa.size()==bb.size())
return aa<bb;
return aa.size()>bb.size();
}
void getNext()
{
int k=-1;
int j=0;
Next[j]=k;
while(j<m)
{
if(k==-1||t[k]==t[j])
{
k++;
j++;
Next[j]=k;
}
else
k=Next[k];
}
}
void getnex()
{
int k=-1;
int j=0;
nex[j]=k;
while(j<m)
{
if(k==-1||temp[k]==temp[j])
{
k++;
j++;
nex[j]=k;
}
else
k=nex[k];
}
}
bool KMP(int start)
{
int i=start,j=0;
while(i<mm&&j<m)
{
if(j==-1||p[i]==t[j])
{
i++,j++;
}
else
j=Next[j];
}
if(j==m)
return true;
return false;
}
bool kmp(int start)
{
int i=start,j=0;
while(i<mm&&j<m)
{
if(j==-1||p[i]==temp[j])
{
i++,j++;
}
else
j=nex[j];
}
if(j==m)
return true;
return false;
}
void solve() {
int n;
while(cin>>n&&n)
{
init();
string mn;
for(int i=0;i<n;i++)
{
string s;
cin>>s;
if(i==0)
mn=s;
vv.push_back(s);
if(s.size()<mn.size())
mn=s;
}
for(int i=0;i<mn.size();i++)
{
for(int j=1;i+j<=mn.size();j++)
{
uu.push_back(mn.substr(i,j));
}
}
sort(uu.begin(),uu.end(),cmp);
int ff=0;
for(int i=0;i<uu.size();i++)
{
int flag=1;
t=uu[i];
m=t.size();
getNext();
for(int j=0;j<vv.size();j++)
{
p=vv[j];
mm=p.size();
if(KMP(0))
{
}
else
{
flag=0;
break;
}
}
if(flag==1)
{
ff=1;
cout<<t<<"\n";
break;
}
}
if(ff==0)
cout<<"IDENTITY LOST"<<"\n";
}
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
int _ = 1;
//cin>>_;
while (_--) {
init();
solve();
}
return 0;
}
题意 给你一个环形串 输出其最小表示法的首字母位置 最大表示法的首字母位置 以及和对应位置等价位置的个数
最小表示法指一个循环串以某一位开始时对应的串的字典序最小 这个串就是该循环串的最小表示法
先看一下求字符串最小表示法的过程 可以看2003年国家集训队论文集中周源的论文
令 p 表示字符串 s 的最小表示法的下标, l = strlen(s) s = s + s
当 i <= p && j <= p && i != j 时 令 k (0 <= k < l) 为最小使得 s[i + k] != s[j + k] 的整数
k 存在时 若 s[i + k] > s[j + k]
那么对于任意的 x (i <= x <= i + k) 都是不可能等于p的 因为对于这些 x 都有对应的 y (j <= y <= j + k) 使得 s[x, i + k) == s[y, j + k) && s[i + k] > s[j + k] 那么以 y 为起始位置的串肯定是小于以 x 为起始位置的串的 那么就可以令 i = i + k + 1继续这个过程了 s[i + k] < s[j + k] 时同理可令 j = j + k + 1 注意若前进后i == j 需要令 j = j + 1 因为要保证i != j
重复上面的过程直到 i >= l || j >= l || k不存在 此时 i, j 中小的那个数就是答案了 因为小的那个数前后所有都已经被确定不可能为 p
开始时不妨令i = 0, j = 1 p > 0 时是符合上面的条件的 p == 0 时可以发现 i 会保持0不变 j 一直前进直到 j > l 或 k 不存在
那么令 i = 0, j = 1 然后通过上面的过程就能得到最小表示法对应的位置了 同理也可以得到最大表示法对应的位置
至于求等价位置的个数 只要知道这个串是某个串循环了多少次就行了 通过kmp的l - next[l]表示最小循环节的长度就可以求出来
#include <bits/stdc++.h>
#include<iostream>
#include<vector>
#include<algorithm>
//#pragma GCC optimize(2)
//#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
typedef long long ll;
const int maxn=1e6+5;
int Next[maxn];
int m,mm;
string t,p;
void init() {
}
void getNext()
{
int k=-1;
int j=0;
Next[j]=k;
while(j<m)
{
if(k==-1||t[k]==t[j])
{
k++;
j++;
Next[j]=k;
}
else
k=Next[k];
}
}
bool KMP(int start)
{
int i=start,j=0;
while(i<mm&&j<m)
{
if(j==-1||p[i]==t[j])
{
i++,j++;
}
else
j=Next[j];
}
if(j==m)
return true;
return false;
}
int getpos(int op)///0最小表示法, 1最大表示法
{
t=t+t;
int i=0,j=1;
while(i<m&&j<m)
{
int k=0;
while(k<m&&t[i+k]==t[j+k])k++;
if(k>=m)
break;
if((t[i+k]>t[j+k])^op)
i+=k+1;
else
j+=k+1;
if(i==j)
j++;
}
return min(i,j);
}
void solve() {
while(cin>>t)
{
m=t.size();
getNext();
int l=m-Next[m];
int temp;
if(m%l==0)
temp=m/l;
else
temp=1;
int L=getpos(0)+1;
int R=getpos(1)+1;
cout<<L<<" "<<temp<<" "<<R<<" "<<temp<<endl;
}
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
int _ = 1;
//cin>>_;
while (_--) {
init();
solve();
}
return 0;
}
题目大意:
有n个有01组成的字符串,每个字符串都代表一个项链,那么该字符串就是一个环状的结构,求可以经过循环旋转,最后不同的串有多少个。
思路:把所有字符串用最小表示法表示出来,然后全部放在vector中,然后从小到大排个序
前n个有多少个不同字符就有多少个不同串,如果前n个字符全一样说明这n个都可以表示成1个
#include <bits/stdc++.h>
#include<iostream>
#include<vector>
#include<algorithm>
//#pragma GCC optimize(2)
//#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
typedef long long ll;
const int maxn=1e6+5;
int Next[maxn];
int m,mm;
string t,p;
vector<string>uu;
void init() {
uu.clear();
}
void getNext()
{
int k=-1;
int j=0;
Next[j]=k;
while(j<m)
{
if(k==-1||t[k]==t[j])
{
k++;
j++;
Next[j]=k;
}
else
k=Next[k];
}
}
bool KMP(int start)
{
int i=start,j=0;
while(i<mm&&j<m)
{
if(j==-1||p[i]==t[j])
{
i++,j++;
}
else
j=Next[j];
}
if(j==m)
return true;
return false;
}
int getpos(int op)///0最小表示法, 1最大表示法
{
t=t+t;
int i=0,j=1;
while(i<m&&j<m)
{
int k=0;
while(k<m&&t[i+k]==t[j+k])k++;
if(k>=m)
break;
if((t[i+k]>t[j+k])^op)
i+=k+1;
else
j+=k+1;
if(i==j)
j++;
}
return min(i,j);
}
void solve() {
int n;
while(cin>>n)
{
init();
for(int i=0;i<n;i++)
{
cin>>t;
p=t;
m=t.size();
int res=getpos(0);
for(int j=0;j<m;j++)
{
p[j]=t[(j+res)%m];
}
uu.push_back(p);
}
sort(uu.begin(),uu.end());
uu.erase(unique(uu.begin(),uu.end()),uu.end());
cout<<uu.size()<<endl;
}
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
int _ = 1;
//cin>>_;
while (_--) {
init();
solve();
}
return 0;
}
题意:给你个字符串,让你求有多少个p可以使S[i]==S[i+P] (0<=i<len-p-1)。
题解:这个题是真的坑,一开始怎么都觉得自己不可能错,然后看了别人的博客打脸了,发现自己掉坑了了…一开始想的是找出最小循环节,只要每次输出多加一个循环节,最后输出len就好了。后来发现最小循环节的倍数并不能表示所有循环节。看到的一组例子ababaaaabab,应该输出7,9,11;但是最小循环节的输出是7,11。
#include <bits/stdc++.h>
#include<iostream>
#include<vector>
#include<algorithm>
//#pragma GCC optimize(2)
//#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
typedef long long ll;
const int maxn=1e6+5;
int Next[maxn];
int m,mm;
string t,p;
int cas=0;
vector<string>uu;
void init() {
uu.clear();
}
void getNext()
{
int k=-1;
int j=0;
Next[j]=k;
while(j<m)
{
if(k==-1||t[k]==t[j])
{
k++;
j++;
Next[j]=k;
}
else
k=Next[k];
}
}
bool KMP(int start)
{
int i=start,j=0;
while(i<mm&&j<m)
{
if(j==-1||p[i]==t[j])
{
i++,j++;
}
else
j=Next[j];
}
if(j==m)
return true;
return false;
}
int getpos(int op)///0最小表示法, 1最大表示法
{
t=t+t;
int i=0,j=1;
while(i<m&&j<m)
{
int k=0;
while(k<m&&t[i+k]==t[j+k])k++;
if(k>=m)
break;
if((t[i+k]>t[j+k])^op)
i+=k+1;
else
j+=k+1;
if(i==j)
j++;
}
return min(i,j);
}
void solve() {
cin>>t;
m=t.size();
getNext();
int r=m-Next[m];
int res=m/r+(m%r!=0);
cout<<"Case #"<<cas<<": "<<res<<endl;
int xx=r;
res--;
while(res)
{
cout<<xx<<" ";
xx+=r;
res--;
}
cout<<m;
cout<<endl;
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
int _ = 1;
cin>>_;
while (_--) {
cas++;
init();
solve();
}
return 0;
}
众所周知,语法在学习英语时非常重要。现在,YYF成为一所小学的老师。学生们善于记忆,所以能说出课本中单词的意思和功能(名词、动词等)。但是,他们不能正确使用这些词。
在YYF的心目中,写句子是学习语法的好方法。因此,他告诉学生每天写20个句子,使用在课堂上学到的单词。由于YYF有很多学生,他将会从他的学生那里获得许多句子。检查所有的句子是件多么可怕的工作啊。你是YYF的朋友之一,所以他请求你帮忙。你的任务是写一个程序来检查句子。
为了使工作简单,YYF选择了语法的一部分:所有的单词可以分为七个部分(名词、代词、形容词、副词、介词、文章和动词)。动词可以是传递的,也可以是过渡性的。因此,我们使用"n",“pron”,“adj”,“adv”,“准备”,“艺术”,“vt"和"vi"来缺少名词、词名、形容词、副词、介词、文章、转序动词和不通性动词。如果一个单词被标记为"v.”,它可以用作传递动词或不通性动词。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<string>
#include<map>
using namespace std;
map<string,int> fun; //把每种词标号
map<string,int> num; //将题目中给出的词标号
map<string,char> str; //记录每种主语宾语谓语介词短语的每种形式,打表
map<string,int> ste; //每种句子形式,打表
void init()
{
fun["n."]=0;
fun["pron."]=1;
fun["adj."]=2;
fun["adv."]=3;
fun["prep."]=4;
fun["art."]=5;
fun["vt."]=6;
fun["vi."]=7;
fun["v."]=8;
str["450"]='A'; //介词短语
str["4520"]='A';
str["41"]='A';
str["1"]='S'; //主/宾语
str["50"]='S';
str["520"]='S';
str["7"]='I'; //不及物谓语
str["37"]='I';
str["6"]='T'; //及物谓语
str["36"]='T';
str["8"]='V'; //通用谓语
str["38"]='V';
//句子可能的总体结构
ste["SI"]=1;
ste["STS"]=1;
ste["SV"]=1;
ste["SVS"]=1;
ste["ASI"]=1;
ste["ASTS"]=1;
ste["ASV"]=1;
ste["ASVS"]=1;
ste["SAI"]=1;
ste["SATS"]=1;
ste["SAV"]=1;
ste["SAVS"]=1;
ste["SIA"]=1;
ste["STAS"]=1;
ste["SVA"]=1;
ste["SVAS"]=1;
ste["STSA"]=1;
ste["SVSA"]=1;
}
int n,m;
int main()
{
string a,b;
init();
scanf("%d%d",&n,&m);
while(n--)
{
cin>>a>>b;
num[a]=fun[b];
}
while(m--)
{
bool flag=false;
b="";
while(cin>>a) //转化为词性表示
{
if(isupper(a[0])) a[0]=a[0]+32;
int len=a.length();
if(a[len-1]=='.')
{
flag=true;
a.erase(len-1,1);
}
if(a[len-1]==',') a.erase(len-1,1);
b+='0'+num[a];
if(flag) break;
}
//cout<<b<<endl;
string c,d;
c=d="";
for(int i=0;i<b.length();i++) //转化为结构表示
{
c+=b[i];
if(str[c])
{
d+=str[c];
c="";
}
}
d+=c;
//cout<<d<<endl;
if(ste[d]) cout<<"YES"<<endl; //判断是否符合
else cout<<"NO"<<endl;
}
return 0;
}
问题在于如果快速判断a串的前缀和后缀是否是回文串,这里只说前缀(因为后缀就是反串的前缀),考虑串a与其反串b的匹配,如果a的某个前缀是回文串,那么因为其反串是b串的一个后缀,所以只要extend[i]=len-i,那么前缀a[0~i]就是一个回文串,同理,在b与a的匹配中,如果extend[i]=len-i,那么后缀a[len-1-i,len-1]就是一个回文串,做两边扩展kmp通过extend数组可以得到以第i个字符结尾或者开始的前后缀是否为回文串,之后O(n)枚举切割点更新最大价值即可(需要预处理出价值前缀和)
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
#define maxn 555555
char a[maxn],b[maxn];
int T,v[33],sum[maxn],flag1[maxn],flag2[maxn],nex[maxn],extend[maxn];
void extend_kmp(char *a,char *b)
{
int i,j,k,la=strlen(a),lb=strlen(b);
for(i=0;i+1<la&&a[i+1]==a[i];i++);
nex[1]=i;
k=1;
for(i=2;i<la;i++)
{
int len=k+nex[k];
nex[i]=min(nex[i-k],max(0,len-i));
while(i+nex[i]<la&&a[nex[i]]==a[i+nex[i]])nex[i]++;
if(i+nex[i]>k+nex[k])k=i;
}
for(i=0;i<la&&i<lb&&a[i]==b[i];i++);
extend[0]=i;
k=0;
for(i=1;i<lb;i++)
{
int len=k+extend[k];
extend[i]=min(nex[i-k],max(0,len-i));
while(i+extend[i]<la&&i+extend[i]<lb&&a[extend[i]]==b[extend[i]+i])
extend[i]++;
if(k+extend[k]<i+extend[i])k=i;
}
}
int main()
{
scanf("%d",&T);
while(T--)
{
memset(flag1,0,sizeof(flag1));
memset(flag2,0,sizeof(flag2));
for(int i=0;i<26;i++)scanf("%d",&v[i]);
scanf("%s",a);
int len=strlen(a);
for(int i=0;i<len;i++)b[i]=a[len-1-i];
sum[0]=v[a[0]-'a'];
for(int i=0;i<len;i++)sum[i]=sum[i-1]+v[a[i]-'a'];
extend_kmp(a,b);
for(int i=0;i<len;i++)if(extend[i]==len-i)flag1[len-1-i]=1;
extend_kmp(b,a);
for(int i=0;i<len;i++)if(extend[i]==len-i)flag2[i]=1;
int ans=v[a[0]-'a'];
for(int i=0;i<len-1;i++)
{
int temp=flag1[i]*sum[i]+flag2[i+1]*(sum[len-1]-sum[i]);
ans=max(ans,temp);
}
printf("%d\n",ans);
}
return 0;
}