版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
题意是在给定的所有字符串中寻找一个最长子串且这个长度大于3,有相同长度的输出字典序最小的子串。
思路:枚举第一个子串的所有大于3的子串,在带入其他字符串中用KMP匹配。
400+ms
AC Code:
#include<iostream>
#include<cstring>
#include<queue>
#include<map>
#include<set>
#include<stack>
#include<cmath>
#include<cstdio>
#include<iomanip>
#include<sstream>
#include<algorithm>
using namespace std;
#define read(x) scanf("%d",&x)
#define Read(x,y) scanf("%d%d",&x,&y)
#define sRead(x,y,z) scanf("%d%d%d",&x,&y,&z)
#define gc(x) scanf(" %c",&x);
#define mmt(x,y) memset(x,y,sizeof x)
#define write(x) printf("%d\n",x)
#define INF 0x3f3f3f3f
#define ll long long
#define mod 998244353
#define pdd pair<double,double>
const int N = 1000;
const int M= 1e6;
int Next[1000];
string s[15];
set<string> st;
void kmp_pre(string x)
{
int m = x.size();
int i = 0,j = Next[0] = -1;
while(i < m){
while(j != -1&& x[i]!=x[j]) j = Next[j];
Next[++i] = ++j;
}
}
bool kmp(string s1,string s2){
int n = s1.size(),m = s2.size();
int i = 0,j = 0;
while(i < n){
while(j!=-1&&s1[i]!=s2[j]) j = Next[j];
++i,++j;
if(j >= m) return 1;
}
return 0;
}
bool solve(string tmp,int k){
int i ;
mmt(Next,0);
kmp_pre(tmp);
for( i = 2;i <= k;++i){
if(!kmp(s[i],tmp)) break;
}
if(i > k) return 1;
else return 0;
}
int main()
{
//freopen("input.txt","r",stdin);
int T;
read(T);
while(T--){
int k;
st.clear();
read(k);
for(int i = 1;i <= k;++i){
cin>>s[i];
}
int MAX = -1;
for(int i = 0;i < s[1].size() - 3;++i){
for(int j = s[1].size();j >=3;--j){
if(i + j >s[1].size() ) continue;//这儿多加了一个 = ,坑死我了,逻辑错误,最为致命
string tmp = s[1].substr(i,j);
if(solve(tmp,k)) {
if(j > MAX) {MAX = j;st.clear();st.insert(tmp);}
else if(j == MAX) st.insert(tmp);//忘了考虑相同的情况,逻辑错误最为致命
}
}
}
if(MAX==-1) puts("no significant commonalities");
else {
cout<<*st.begin()<<endl;
}
}
}
KMP + 二分
70+ms
其实可以发现我们枚举子串长度与答案之间满足某种 单调性,即存在某种长度的子串满足题目要求,那么这个子串的子串肯定也满足,所以我们可以二分长度来加速枚举的过程,最后需要注意的是,当l + 1 > r跳出时,并没有检验长度为 l 的串,所以最后需要检验一下 l 长度的子串。
AC Code:
#include<iostream>
#include<cstring>
#include<queue>
#include<map>
#include<set>
#include<stack>
#include<cmath>
#include<cstdio>
#include<iomanip>
#include<sstream>
#include<algorithm>
using namespace std;
#define read(x) scanf("%d",&x)
#define Read(x,y) scanf("%d%d",&x,&y)
#define sRead(x,y,z) scanf("%d%d%d",&x,&y,&z)
#define gc(x) scanf(" %c",&x);
#define mmt(x,y) memset(x,y,sizeof x)
#define write(x) printf("%d\n",x)
#define INF 0x3f3f3f3f
#define ll long long
#define mod 998244353
#define pdd pair<double,double>
const int N = 1000;
const int M= 1e6;
int Next[1000];
string s[15];
set<string> st;
void kmp_pre(string x)
{
int m = x.size();
int i = 0,j = Next[0] = -1;
while(i < m){
while(j != -1&& x[i]!=x[j]) j = Next[j];
Next[++i] = ++j;
}
}
bool kmp(string s1,string s2){
int n = s1.size(),m = s2.size();
int i = 0,j = 0;
while(i < n){
while(j!=-1&&s1[i]!=s2[j]) j = Next[j];
++i,++j;
if(j >= m) return 1;
}
return 0;
}
bool solve(string tmp,int k){
int i ;
mmt(Next,0);
kmp_pre(tmp);
for( i = 2;i <= k;++i){
if(!kmp(s[i],tmp)) break;
}
if(i > k) return 1;
else return 0;
}
int MAX = -1;
bool ok(int l,int k){
bool r = 1;
for(int i = 0;i < s[1].size();++i){
if(l + i > s[1].size()) continue;
string tmp = s[1].substr(i,l);
if(solve(tmp,k)) {
if(l > MAX) MAX = l,st.clear();
st.insert(tmp);
r = 0;
}
}
return r == 1?0:1;
}
int main()
{
// freopen("input.txt","r",stdin);
int T;
read(T);
while(T--){
int k;
st.clear();
read(k);
for(int i = 1;i <= k;++i){
cin>>s[i];
}
int l = 3,r = s[1].size();
MAX = -1;
while(l < r){
int mid = l + r >> 1;
if(ok(mid,k)) l = mid + 1;
else r = mid;
}
ok(l,k);//检验 长度为l 的子串
if(st.size()==0) puts("no significant commonalities");
else {
cout<<*st.begin()<<endl;
}
}
}