• 2023NOIP A层联测10 子序列


    题目大意

    给定一个长度为 n n n的仅由小写字母构成的字符串 S S S。我们定义一个字符串是好的,当且仅当它可以用两个不同的字母 x x x y y y表示成 x y x y x y x … xyxyxyx\dots xyxyxyx的形式。比如字符串 a b a b abab abab t o t tot tot a a a是好的,但字符串 a b c abc abc a a aa aa不是好的。

    现在有 q q q组询问,每次给定 1 ≤ l ≤ r ≤ n 1\leq l\leq r\leq n 1lrn,求 S S S的子串 S [ l ⋯ r ] S[l\cdots r] S[lr]的最长的一个好的子序列的长度是多少,以及它可以被哪两个字符 x x x y y y来表示。如果有多个最长的串,则输出字典序最小的一个串的 x x x y y y

    1 ≤ n ≤ 1.5 × 1 0 6 , 1 ≤ m ≤ 1 0 5 1\leq n\leq 1.5\times 10^6,1\leq m\leq 10^5 1n1.5×106,1m105


    题解

    对每个字符 i i i,考虑在只保留它时序列被分为若干段(???i??i???i??),预处理 z t i , j , k zt_{i,j,k} zti,j,k表示只保留字符 i i i时字符 j j j能否在第 k k k段出现,并用 f i , j , k f_{i,j,k} fi,j,k记录 z t i , j , k zt_{i,j,k} zti,j,k的前缀和。预处理 l t i , j lt_{i,j} lti,j r t i , j rt_{i,j} rti,j,分别表示在 i i i之前的第一个字符 j j j和在 i i i之后的第一个字符 j j j。因为所有 i i i的数量的和为 n n n,所以这部分的时间复杂度为 O ( n v ) O(nv) O(nv),其中 v v v为字符的个数,也就是小写字母的个数,即 26 26 26

    对于一组询问 l , r l,r l,r,枚举一种字符 i i i,用 l t lt lt r t rt rt来确定这段区间中 c c c第一次出现的位置 v l vl vl和最后一次出现的位置 v r vr vr,求出这两个位置的段标号 b l bl bl b r br br,再枚举第二种字符 j j j,那么整段的贡献为 f i , j , b r − f i , j , b l − 1 f_{i,j,br}-f_{i,j,bl-1} fi,j,brfi,j,bl1。对于两边的散块,用 l t lt lt r t rt rt来求贡献即可。这部分的时间复杂度为 O ( m v 2 ) O(mv^2) O(mv2)

    总时间复杂度为 O ( n v + m v 2 ) O(nv+mv^2) O(nv+mv2)

    code

    #include
    using namespace std;
    const int N=1500000,M=100000,K=26;
    int n,m,l,r,ans,zt[N+5],id[N+5],lt[K][N+5],rt[K][N+5];
    char v1,v2,s[N+5];
    vector<int>w[K],f[K][K];
    int main()
    {
    //	freopen("seq.in","r",stdin);
    //	freopen("seq.out","w",stdout);
    	scanf("%s",s+1);
    	n=strlen(s+1);
    	for(int i=1;i<=n;i++){
    		id[i]=w[s[i]-'a'].size();
    		w[s[i]-'a'].push_back(i);
    	}
    	for(int i=0;i<26;i++){
    		for(int k=0;k+1<w[i].size();k++){
    			for(int j=w[i][k]+1;j<w[i][k+1];j++){
    				zt[s[j]-'a']=1;
    			}
    			for(int j=0;j<26;j++){
    				f[i][j].push_back(zt[j]);zt[j]=0;
    				if(k) f[i][j][k]=f[i][j][k]+f[i][j][k-1];
    			}
    		}
    	}
    	for(int i=0;i<26;i++){
    		lt[i][0]=0;
    		for(int j=1;j<=n;j++){
    			lt[i][j]=lt[i][j-1];
    			if(s[j]-'a'==i) lt[i][j]=j;
    		}
    		rt[i][n+1]=n+1;
    		for(int j=n;j>=1;j--){
    			rt[i][j]=rt[i][j+1];
    			if(s[j]-'a'==i) rt[i][j]=j;
    		}
    	}
    	scanf("%d",&m);
    	while(m--){
    		scanf("%d%d",&l,&r);
    		ans=0;
    		for(int i=0;i<26;i++){
    			int vl=rt[i][l],vr=lt[i][r];
    			if(vl>vr) continue;
    			if(vl>=n+1||vr<=0) continue;
    			int bl=id[vl],br=id[vr];
    			for(int j=0;j<26;j++){
    				if(i==j) continue;
    				if(rt[i][l]>rt[j][l]) continue;
    				int tmp=0;
    				if(br) tmp+=f[i][j][br-1];
    				if(bl) tmp-=f[i][j][bl-1];
    				tmp=tmp*2+1;
    				if(rt[j][vr]<=r||lt[j][vl]>=l) ++tmp;
    				if(tmp>ans){
    					ans=tmp;
    					v1='a'+i;v2='a'+j;
    				}
    			}
    		}
    		printf("%d %c%c\n",ans,v1,v2);
    	}
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
  • 相关阅读:
    【34W字CISSP备考笔记】域1:安全与风险管理
    python的卡夫卡安装教程和使用教程
    计算机组成原理——计算机系统层次结构
    SVG公众号排版 | 可重复点击显示和关闭图片,在Apple上无法正常关闭
    关于天地图新手使用
    推荐一款M1芯片电脑快速搭建集群的虚拟机软件
    Java 数字金额,字符串格式化
    【原创】只用Asp.NET Core Web API与Vue 3.0搭建前后分离项目
    【Linux】swap有什么用?如何建立swap分区?
    安全漏洞-linux漏洞修复命令
  • 原文地址:https://blog.csdn.net/tanjunming2020/article/details/133862973