Annihilate
XV.Annihilate
我当年为什么会手贱开这卡常大毒瘤题呀
思路1. 用vector
存下每个字符串在后缀排序后的下标,然后每次枚举两个串,用一个vector
里面的数在另一个里面two-pointers
找到它两侧的数,然后用ST表求LCP。
时间复杂度,空间复杂度,光荣MLE。
代码:
#include<bits/stdc++.h>
using namespace std;
int all,id[1001000],res[100][100];
namespace Suffix_Array{
const int N=1001000;
int x[N],y[N],sa[N],ht[N],rk[N],buc[N],n,m;
char s[N];
bool mat(int a,int b,int k){
if(y[a]!=y[b])return false;
if((a+k<n)^(b+k<n))return false;
if((a+k<n)&&(b+k<n))return y[a+k]==y[b+k];
return true;
}
void SA(){
for(int i=0;i<n;i++)buc[x[i]=s[i]]++;
for(int i=1;i<=m;i++)buc[i]+=buc[i-1];
for(int i=n-1;i>=0;i--)sa[--buc[x[i]]]=i;
for(int k=1;k<n;k<<=1){
int num=0;
for(int i=n-k;i<n;i++)y[num++]=i;
for(int i=0;i<n;i++)if(sa[i]>=k)y[num++]=sa[i]-k;
for(int i=0;i<=m;i++)buc[i]=0;
for(int i=0;i<n;i++)buc[x[y[i]]]++;
for(int i=1;i<=m;i++)buc[i]+=buc[i-1];
for(int i=n-1;i>=0;i--)sa[--buc[x[y[i]]]]=y[i],y[i]=0;
swap(x,y);
x[sa[0]]=num=0;
for(int i=1;i<n;i++)x[sa[i]]=mat(sa[i],sa[i-1],k)?num:++num;
m=num;
}
for(int i=0;i<n;i++)rk[sa[i]]=i;
for(int i=0,k=0;i<n;i++){
if(!rk[i])continue;
if(k)k--;
int j=sa[rk[i]-1];
while(j+k<n&&i+k<n&&s[j+k]==s[i+k])k++;
ht[rk[i]]=k;
}
}
}
using namespace Suffix_Array;
vector<int>v[60];
int mn[1001000][20],LG[1001000];
int LCP(int l,int r){//get the LCP of suffix l and r;
l++;
int k=LG[r-l+1];
return min(mn[l][k],mn[r-(1<<k)+1][k]);
}
int main(){
scanf("%d",&all);
for(int i=1;i<=all;i++){
scanf("%s",s+n);
m=strlen(s+n);
for(int j=n;j<n+m;j++)id[j]=i;
n+=m;
s[n++]=i;
}
m='z';
SA();
for(int i=2;i<n;i++)LG[i]=LG[i>>1]+1;
for(int i=1;i<n;i++)mn[i][0]=ht[i];
for(int j=1;j<=LG[n-1];j++)for(int i=1;i+(1<<j)-1<n;i++)mn[i][j]=min(mn[i][j-1],mn[i+(1<<(j-1))][j-1]);
for(int i=0;i<n;i++)if(id[sa[i]])v[id[sa[i]]].push_back(i);
for(int i=1;i<=all;i++)for(int j=i+1;j<=all;j++){
for(int a=0,b=0;a<v[i].size();a++){
while(b<v[j].size()&&v[i][a]>v[j][b])b++;
if(b<v[j].size())res[i][j]=max(res[i][j],LCP(v[i][a],v[j][b]));
if(b)res[i][j]=max(res[i][j],LCP(v[j][b-1],v[i][a]));
}
}
for(int i=1;i<=all;i++){for(int j=1;j<=all;j++)if(i!=j)printf("%d ",res[min(i,j)][max(i,j)]);puts("");}
return 0;
}
思路2. 用单调队列求LCP
大体思路和上题一致,只不过换用单调队列求LCP。
时间复杂度,光荣TLE。
代码:
#include<bits/stdc++.h>
using namespace std;
int all,id[1001000],res[100][100];
namespace Suffix_Array{
const int N=1001000;
int x[N],y[N],sa[N],ht[N],rk[N],buc[N],n,m;
char s[N];
bool mat(int a,int b,int k){
if(y[a]!=y[b])return false;
if((a+k<n)^(b+k<n))return false;
if((a+k<n)&&(b+k<n))return y[a+k]==y[b+k];
return true;
}
void SA(){
for(int i=0;i<n;i++)buc[x[i]=s[i]]++;
for(int i=1;i<=m;i++)buc[i]+=buc[i-1];
for(int i=n-1;i>=0;i--)sa[--buc[x[i]]]=i;
for(int k=1;k<n;k<<=1){
int num=0;
for(int i=n-k;i<n;i++)y[num++]=i;
for(int i=0;i<n;i++)if(sa[i]>=k)y[num++]=sa[i]-k;
for(int i=0;i<=m;i++)buc[i]=0;
for(int i=0;i<n;i++)buc[x[y[i]]]++;
for(int i=1;i<=m;i++)buc[i]+=buc[i-1];
for(int i=n-1;i>=0;i--)sa[--buc[x[y[i]]]]=y[i],y[i]=0;
swap(x,y);
x[sa[0]]=num=0;
for(int i=1;i<n;i++)x[sa[i]]=mat(sa[i],sa[i-1],k)?num:++num;
m=num;
}
for(int i=0;i<n;i++)rk[sa[i]]=i;
for(int i=0,k=0;i<n;i++){
if(!rk[i])continue;
if(k)k--;
int j=sa[rk[i]-1];
while(j+k<n&&i+k<n&&s[j+k]==s[i+k])k++;
ht[rk[i]]=k;
}
}
}
using namespace Suffix_Array;
vector<int>v[60];
int main(){
scanf("%d",&all);
for(int i=1;i<=all;i++){
scanf("%s",s+n);
m=strlen(s+n);
for(int j=n;j<n+m;j++)id[j]=i;
n+=m;
s[n++]=i;
}
m='z';
SA();
for(int i=1;i<=all;i++)v[i].push_back(i);
for(int i=0;i<n;i++)if(id[sa[i]])v[id[sa[i]]].push_back(i);
for(int i=1;i<=all;i++)for(int j=i+1;j<=all;j++){
deque<int>p,q;
for(int a=1,b=0;a<v[i].size();a++){
for(int k=v[i][a-1]+1;k<=v[i][a];k++){
while(!p.empty()&&ht[p.back()]>=ht[k])p.pop_back();
p.push_back(k);
}
while(b<v[j].size()&&v[i][a]>v[j][b]){
while(!p.empty()&&p.front()<=v[j][b])p.pop_front();
b++;
if(b==v[j].size())break;
for(int k=v[j][b-1]+1;k<=v[j][b];k++){
while(!q.empty()&&ht[q.back()]>=ht[k])q.pop_back();
q.push_back(k);
}
}
while(!q.empty()&&q.front()<=v[i][a])q.pop_front();
if(!p.empty())res[i][j]=max(res[i][j],ht[p.front()]);
if(!q.empty())res[i][j]=max(res[i][j],ht[q.front()]);
}
}
for(int i=1;i<=all;i++){for(int j=1;j<=all;j++)if(i!=j)printf("%d ",res[min(i,j)][max(i,j)]);puts("");}
return 0;
}
思路3.正解
我们设一个表示当前位置到上一个来自串的后缀的区间中的。到了每一个位置地遍历数组更新并计算答案。复杂度,可以通过。
但是这题卡常强烈吐槽!
代码(开O2通过):
#include<bits/stdc++.h>
using namespace std;
int all,id[1010000],res[100][100],las[100];
namespace Suffix_Array{
const int N=1010000;
int x[N],y[N],sa[N],ht[N],rk[N],buc[N],n,m,s[N];
void SA(){
for(int i=1;i<=n;i++)buc[x[i]=s[i]]++;
for(int i=1;i<=m;i++)buc[i]+=buc[i-1];
for(int i=n;i;i--)sa[buc[x[i]]--]=i;
for(int k=1;k<=n;k<<=1){
int num=0;
for(int i=n-k+1;i<=n;i++)y[++num]=i;
for(int i=1;i<=n;i++)if(sa[i]>k)y[++num]=sa[i]-k;
for(int i=0;i<=m;i++)buc[i]=0;
for(int i=1;i<=n;i++)buc[x[y[i]]]++;
for(int i=1;i<=m;i++)buc[i]+=buc[i-1];
for(int i=n;i;i--)sa[buc[x[y[i]]]--]=y[i],y[i]=0;
swap(x,y);
x[sa[1]]=num=1;
for(int i=2;i<=n;i++)x[sa[i]]=(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k])?num:++num;
if(num>=n)break;
m=num;
}
for(int i=1;i<=n;i++)rk[sa[i]]=i;
for(int i=1,k=0;i<=n;i++){
if(rk[i]==1)continue;
if(k)k--;
int j=sa[rk[i]-1];
while(j+k<=n&&i+k<=n&&s[j+k]==s[i+k])k++;
ht[rk[i]]=k;
}
}
}
using namespace Suffix_Array;
char str[1001000];
int main(){
scanf("%d",&all);
for(int i=1;i<=all;i++){
scanf("%s",str);
m=strlen(str);
for(int j=0;j<m;j++)n++,s[n]=str[j]-'a'+1,id[n]=i;
s[++n]=i+26;
}
m=all+26;
SA();
// for(int i=1;i<=n;i++)printf("%d ",s[i]);puts("");
// for(int i=1;i<=n;i++)printf("%d ",rk[i]);puts("");
for(int i=2;i<=n;i++){
for(int j=1;j<=all;j++)las[j]=min(las[j],ht[i]);
las[id[sa[i-1]]]=ht[i];
for(int j=1;j<=all;j++)res[j][id[sa[i]]]=max(res[j][id[sa[i]]],las[j]);
}
for(int i=1;i<=all;i++){for(int j=1;j<=all;j++)if(i!=j)printf("%d ",max(res[i][j],res[j][i]));puts("");}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?