首页 > 其他分享 >CF1424M Ancient Language 题解

CF1424M Ancient Language 题解

时间:2024-01-22 22:47:55浏览次数:29  
标签:字符 Ancient 前缀 int 题解 位置 CF1424M 一个 字符串

1. 题意描述

一本字典中有 \(r\) \((1 \leq r \leq 10^6)\) 个单词,单词长度不超过 $10^3 $,所有字母都是小写英文字母,但字母排序不是按英文字母排序,求所有出现字母的一种排序,如果不存在,输出 "IMPOSSIBLE"。

2. 题目分析

由排序规则可知,对于字符串 \(s\) 和 \(t\),\(s\) 排在 \(t\) 前面,必是一下两种情况之一:

  • \(s\) 是 \(t\) 前缀。
  • 设 \(s\) 和 \(t\) 第一个不同字符位置为 \(i\),则 \(s_i\) 的顺序在 \(t_i\) 之前。

所以,如果存在字符串 \(s\) 和 \(t\),\(s\) 排在 \(t\) 前面但 \(t\) 是 \(s\) 前缀,则无解;然后可以记录每个导致 \(s\) 排在 \(t\) 前面的每对字母,判断是否矛盾并求解。

但是这个的时间复杂度是极高的,于是想办法优化。实际上,只需要对比相邻两个字符串即可保证正确性。证明如下:

如果存在字符串 \(s\) 和 \(t\),\(s\) 排在 \(t\) 前面但 \(t\) 是 \(s\) 前缀,且 \(s\) 和 \(t\) 不相邻,可能会有:

  1. \(s\) 后一个字符串是 \(s\) 前缀,且 \(t\) 是这个字符串前缀,可以判断无解。
  2. \(s\) 后一个字符串不是 \(s\) 前缀,且 \(t\) 是这个字符串前缀,用归纳法可以证明这种情况会出现矛盾。
  3. \(t\) 不是 \(s\) 后一个字符串的前缀,则在 \(s\) 后一个字符串和 \(t\) 必然存在一个位置,使两个字符串这个位置上的字符不同,但此时 \(s\) 后一个字符串这个位置的字符同时在 \(s\) 这个位置的字符之前和之后,就能推出无解。

如果存在字符串 \(s\) 和 \(t\),\(s\) 排在 \(t\) 前面,\(t\) 不是 \(s\) 前缀,可能会有:

  1. \(s\) 和 \(t\) 相邻,直接得到关系。
  2. \(s\) 后一个字符串是 \(s\) 前缀,可推出无解。
  3. \(s\) 是 \(s\) 后一个字符串前缀,可归纳证明可有其它信息得到 \(s\) 和 \(t\) 能得到的信息。
  4. \(s\) 后一个字符串和 \(s\) 不互相为前缀,且 \(s\) 后一个字符串和 \(s\) 第一个不相同的位置不在 \(s\) 和 \(t\) 第一个不相同的位置之前,可归纳证明可有其它信息得到 \(s\) 和 \(t\) 能得到的信息。
  5. \(s\) 后一个字符串和 \(s\) 不互相为前缀,且 \(s\) 后一个字符串和 \(s\) 第一个不相同的位置在 \(s\) 和 \(t\) 第一个不相同的位置之前,归纳法可推出能得到 \(s\) 后一个字符串和 \(s\) 第一个不相同位置上的字符同时在 \(s\) 这个位置上的字符之前和之后,则能推出无解。

综上,所有不相邻的两个字符串,要么可以用其它条件推出无解,要么可以用其它条件推出这两个字符串看获得的信息。

对这些条件分析,只需要用 Floyd 算法求传递闭包,然后如果存在字符 \(i\) 在 \(i\) 前,则无解;否则每次选出一个不在所有未选择字符之前的数作为插入当前得到顺序开头,则最终可以得到正确顺序。

3. 示例代码

#include <bits/stdc++.h>

using namespace std;

const int N = 1005, T = N * N, M = 105, C = 30;

int n, k;
char s[T][M];
bool f[C][C], st[C], ins[C];
char res[C];

int main()
{
	scanf("%d%d", &n, &k);
	for(int i = 1; i <= n; i ++ )
	{
		int p;
		scanf("%d", &p);
		for(int j = 1; j <= k; j ++ ) scanf("%s", s[p * k + j] + 1);
	}
	
	for(int i = 1; i <= n * k; i ++ )
	{
		if(i < n * k)
			for(int j = 1; j < M; j ++ )
			{
				if(!s[i][j]) break; // s[i] 是 s[i + 1] 前缀 
				if(!s[i + 1][j]) // s[i + 1] 是 s[i] 前缀 
				{
					puts("IMPOSSIBLE");
					return 0;
				}
				if(s[i][j] != s[i + 1][j]) // s[i + 1] 和 s[i] 第一个不相同字符的位置是 j   
				{
					f[s[i][j] - 'a' + 1][s[i + 1][j] - 'a' + 1] = true; // s[i][j] 在 s[i + 1][j] 之前 
					break;
				}
			}
		for(int j = 1; s[i][j]; j ++ ) ins[s[i][j] - 'a' + 1] = true; // 找到出现的字符  
	}
	
	for(int k = 1; k <= 26; k ++ ) // Floyd 求传递闭包 
		for(int i = 1; i <= 26; i ++ )
			for(int j = 1; j <= 26; j ++ )
				f[i][j] |= f[i][k] & f[k][j];
	
	for(int i = 1; i <= 26; i ++ )
		if(f[i][i]) // i 在 i 之前,无解  
		{
			puts("IMPOSSIBLE");
			return 0;
		}
	
	int cnt = 0;
	for(int i = 1; i <= 26; i ++ ) cnt += ins[i];
	for(int i = 1; i <= cnt; i ++ )
	{
		int c;
		for(int j = 1; j <= 26; j ++ )
			if(ins[j] && !st[j])
			{
				int p = 0;
				for(int k = 1; k <= 26; k ++ )
					p += !st[k] && f[j][k];
				if(!p) // j 不在所有未选出的字符前面 
				{
					c = j;
					break;
				}
			}
		st[c] = true;
		res[cnt - i] = c + 'a' - 1;
	}
	
	puts(res);
	return 0;
}

标签:字符,Ancient,前缀,int,题解,位置,CF1424M,一个,字符串
From: https://www.cnblogs.com/recollect-the-past/p/17981255

相关文章

  • CF618C Constellation 题解
    题意描述给定\(n\)个点(\(n\leq10^5\)),找到三个点满足这三个点不在同一直线上且三个点构成的三角形中不包含其他点,保证所有点不会在同一直线上。题目分析首先必然每一个点都可以作为一组解中的点。不妨其中一个点编号为\(1\)。找一个点作为第二个点,为了使没有点在这条边上,这......
  • CF1036F Relatively Prime Powers 题解
    题目分析对于一个不合法的数\(x(x\ge2)\),设\(x=\prodp_i^{r_i}\),令\(g=\gcd(r_1,r_2,\ldots,r_k)\),则\(x=\left(\prodp_i^{r_i/g}\right)^g\),所以\(x\)是一个正整数的\(g\)次方。所以可以枚举上文的\(g\),把每一类不合法方案排除掉,就是答案。设\(f(i)\)表示\(2\)......
  • AT_arc098_d Donation 题解
    一道在kruskal重构树上DP的题。首先,捐钱比较难想,可以反过来思考倒着走领钱的思路。显然,在第一次经过一个节点的时候领钱是最优的,对于节点\(i\),令\(c_i=\max\{a_i-b_i,0\}\),若当前的钱数是\(v\),到节点\(i\)的条件是\(v\gec_i\),如果不满足则把\(v\)补充到\(c_i\),同......
  • P5618 SDOI2015 道路修建题解
    题目分析虽然数据范围只有\(n\le60000\),但是完全可以直接用线段树做。首先考虑那种状态的图在左边和右边加入节点和边之后可以连通。容易发现,这种图有这两个性质:至少有一条路径,经过最左端和最右端中的点。所有点至少和最左端和最右端的点连通。于是可以划分成以下几种状态......
  • [ARC155C] Even Sum Triplet 题解
    一道大分类讨论。如果有一个可以交换的段包含奇数,那么你可以把所有奇数移到最左边并任意调整相对顺序,然后回到任意一种有一个可以交换的段包含奇数的状态。这种情况,如果偶数的数量为\(2\),这两个偶数是不能交换相对位置的,有至少\(3\)个偶数就能交换偶数间相对位置。所以只需要......
  • P7618 [COCI2011-2012#2] FUNKCIJA 题解
    看起来比较复杂,但实际上是一个树上dp的模型。因为每一重循环都最多有一个依赖,可以转化为树上的父子关系,于是就形成了一个森林。对于非叶子节点,设\(f_{u,i}\)表示第\(u\)循环变量的值是\(i\)时所有直接或间接依赖于它的循环变量(即以它为根的子树除开它的部分)循环次数,对非......
  • 题解-[ABC288D] Range Add Query
    题目:[ABC288D]RangeAddQuery-洛谷|计算机科学教育新生态(luogu.com.cn) 大意:一些数,选一个区间A(L~R),并再选一个区间B(长度k),这个区间B的每个数加减(加负数=减一个数)一个数,最终使得区间A全部数为0举个例(样例)0.   3-11-2201.  0-4-2-220 (-3)2.  ......
  • 2024 蓝桥杯模拟赛 1 (div1) 题解
    A.把字符串小写转换成大写即可#include<bits/stdc++.h>usingnamespacestd;voidsolve(){strings;cin>>s;for(inti=0;i<s.size();i++){if(s[i]>='a'&&s[i]<='z'){s[i]=(char)(s[i]-'a......
  • 2024 省选联测部分题解
    目录目录R15T1树V图R15T2矩阵缺失题目:R15T3.R15T1树V图原题:SNOI2024D1T1.注意到答案肯定是形如每个连通块选一个点组成,把连通块缩起来后令\(dp_{u,x}\)表示连通块\(u\)选\(x\)的方案数,每次合并子树转移即可.因为只有\(n^2\)个合法点对所以时间复杂度......
  • R语言包安装失败常见问题解决
    更改或指定镜像源出现这个问题很有可能是你现在用的镜像中未纳入这个包,一是可以多换个源试试。如:install.packages('package-name',repos='http://cran.us.r-project.org')或,在Rstudio中可以:或,命令行可直接指定Rstudio:install.packages('package_name',dependencies=TRUE......