Leetcode每日一题:最大字符串匹配数目
题干如下:
给你一个下标从 0 开始的数组 words ,数组中包含互不相同的字符串。
如果字符串 words[i]
与字符串 words[j]
满足以下条件,我们称它们可以匹配:
字符串 words[i]
等于 words[j]
的反转字符串。
0 <= i < j < words.length
请你返回数组 words
中的 最大 匹配数目。
注意,每个字符串最多匹配一次。
示例 1:
输入:words = ["cd","ac","dc","ca","zz"]
输出:2
解释:在此示例中,我们可以通过以下方式匹配 2 对字符串:
- 我们将第 0 个字符串与第 2 个字符串匹配,因为 word[0] 的反转字符串是 "dc" 并且等于 words[2]。
- 我们将第 1 个字符串与第 3 个字符串匹配,因为 word[1] 的反转字符串是 "ca" 并且等于 words[3]。
可以证明最多匹配数目是 2 。
示例 2:
输入:words = ["ab","ba","cc"]
输出:1
解释:在此示例中,我们可以通过以下方式匹配 1 对字符串:
- 我们将第 0 个字符串与第 1 个字符串匹配,因为 words[1] 的反转字符串 "ab" 与 words[0] 相等。
可以证明最多匹配数目是 1 。
示例 3:
输入:words = ["aa","ab"]
输出:0
解释:这个例子中,无法匹配任何字符串。
提示:
1 <= words.length <= 50
words[i].length == 2
words
包含的字符串互不相同。
words[i]
只包含小写英文字母。
Star Rating: 2.68(个人观点)
SR转换:
Leetcode简单 -> 约等于1~2星难度
Leetcode普通 -> 约等于3~4星难度
Leetcode困难 -> 约等于5~6星难度
首先,仍然有一种很暴力的解法,复杂度是O(n^2)
,,也就是固定一个字符串,尝试去找另一个字符串。
但是,这种算法的效率是很低的,因此我们需要想一个更好的方法
我们会发现:
(1)由于words不能包含相同的字符串,因此两个字母相同的字符串不能和任何其他字符串匹配。
Leetcode的题比较偏向数据结构的应用,所以我们要尽量往这个方向去思考...不过对于这个题来说仍然想不到任何思路。
但是我们可以先写一下BF的方法:
class Solution(object):
def maximumNumberOfStringPairs(self, words):
"""
:type words: List[str]
:rtype: int
"""
count = 0
for i in range (len(words)):
if words[i][0] == words[i][1]:
continue
for j in range (len(words)):
if words[i][0] == words[j][1] and words[i][1] == words[j][0]:
count += 1
return count // 2 #每一对匹配字符串计算了两次,所以返回的值要除以2
不过...有没有更好的方法呢?
能够正确提交,32ms(35.71% defeated) and 13.21MB(7.14% defeated)
Current: 23pp(New!)
Overall: 124pp(+19pp)
现在我们思考一下,有没有更好的方法呢?
ChatGPT给出了一个能够拿到34pp的方法:
这个问题可以通过使用字典(哈希表)来提高效率。我们可以将每个字符串的反转形式作为键,然后统计每个反转形式的出现次数。在遍历过程中,如果当前字符串的反转形式已经在字典中出现过,则说明可以进行匹配,匹配的次数就是该反转形式在字典中的出现次数。最后取所有匹配次数的和即为最大匹配数目。
以下是相应的代码:(ChatGPT一开始产生的代码是错的,所以我们又稍微改了一点)
from collections import defaultdict
class Solution(object):
def maximumNumberOfStringPairs(self, words):
"""
:type words: List[str]
:rtype: int
"""
reverse_counts = defaultdict(int)
for word in words:
if word[0] != word[1]:
reverse_form = word[1] + word[0]
if reverse_counts[reverse_form] == 1:
reverse_counts[reverse_form] += 1
else:
reverse_counts[word[0] + word[1]] += 1
# 这样精心设计以后,一对匹配的字符串,会在reverse_counts中产生一个值为2的键
max_matches = sum(count for count in reverse_counts.values() if count % 2 == 0) // 2 #找到值为2的键的总个数
return max_matches
这种方法的时间复杂度为 O(n),其中 n 是字符串数组的长度。我们通过一次遍历统计了每个反转形式的出现次数,然后计算匹配次数的和。因为字典的查找操作是 O(1) 的平均复杂度,所以总体上是线性时间的。
defaultdict
是 Python 中的一个特殊字典,它可以在创建时指定默认值的类型,当访问一个不存在的键时,会返回默认值。在这个问题中,我们使用 defaultdict(int)
创建了一个默认值为 0 的字典,即如果某个键不存在,则返回 0。
在上述代码中,reverse_counts
是一个字典,它的键是字符串的反转形式,值是该反转形式出现的次数。由于我们在创建 defaultdict(int)
时指定了默认值为 0,所以当访问一个不存在的键时,会返回 0。
这样做的好处是,在遍历 words
数组时,我们无需判断每个键是否存在,直接通过 reverse_counts[reverse_form]
就能获取到相应的值,而且不存在的键会自动被初始化为 0。
使用 defaultdict
可以简化代码,使其更加清晰和简洁。
Yes~ 性能有了显著提升 11ms(100.00% Defeated)和11.54MB(100.00% Defeated)
Current: 34pp(+11pp)
Overall: 135pp(+11pp)
Leetcode 1237. 找出给定方程的正整数解
Star Rating: 3.62
题目描述如下:
给你一个函数 f(x, y) 和一个目标结果 z,函数公式未知,请你计算方程 f(x,y) == z 所有可能的正整数数对 x 和 y。满足条件的结果数对可以按任意顺序返回。
尽管函数的具体式子未知,但它是单调递增函数,也就是说:
f(x, y) < f(x + 1, y)
f(x, y) < f(x, y + 1)
函数接口定义如下:
interface CustomFunction {
public:
// Returns some positive integer f(x, y) for two positive integers x and y based on a formula.
int f(int x, int y);
};
你的解决方案将按如下规则进行评判:
判题程序有一个由 CustomFunction
的 9 种实现组成的列表,以及一种为特定的 z 生成所有有效数对的答案的方法。
判题程序接受两个输入:function_id
(决定使用哪种实现测试你的代码)以及目标结果 z
。
判题程序将会调用你实现的 findSolution
并将你的结果与答案进行比较。
如果你的结果与答案相符,那么解决方案将被视作正确答案,即 Accepted 。
示例 1:
输入:function_id = 1, z = 5
输出:[[1,4],[2,3],[3,2],[4,1]]
解释:function_id = 1 暗含的函数式子为 f(x, y) = x + y
以下 x 和 y 满足 f(x, y) 等于 5:
x=1, y=4 -> f(1, 4) = 1 + 4 = 5
x=2, y=3 -> f(2, 3) = 2 + 3 = 5
x=3, y=2 -> f(3, 2) = 3 + 2 = 5
x=4, y=1 -> f(4, 1) = 4 + 1 = 5
示例 2:
输入:function_id = 2, z = 5
输出:[[1,5],[5,1]]
解释:function_id = 2 暗含的函数式子为 f(x, y) = x * y
以下 x 和 y 满足 f(x, y) 等于 5:
x=1, y=5 -> f(1, 5) = 1 * 5 = 5
x=5, y=1 -> f(5, 1) = 5 * 1 = 5
提示:
1 <= function_id <= 9
1 <= z <= 100
题目保证 f(x, y) == z
的解处于 1 <= x, y <= 1000
的范围内。
在 1 <= x, y <= 1000
的前提下,题目保证 f(x, y)
是一个 32 位有符号整数。
"""
This is the custom function interface.
You should not implement it, or speculate about its implementation
class CustomFunction:
# Returns f(x, y) for any given positive integers x and y.
# Note that f(x, y) is increasing with respect to both x and y.
# i.e. f(x, y) < f(x + 1, y), f(x, y) < f(x, y + 1)
def f(self, x, y):
"""
class Solution(object):
def findSolution(self, customfunction, z):
"""
:type num: int
:type z: int
:rtype: List[List[int]]
"""
不过为什么看到这个题的时候,我完全没有思路呢???...
比如说我们的输入z = 6, customfunction是某个未知的运算...
假如说我们先运行customfunction.f(1,1).它在大部分情况等于1,但也有不等于的情况
如果f(1,1)大于z,那么直接返回一个空列表(因为运算函数是递增函数,所以这个时候不可能有solution)
如果f(1,1)小于z???
那么我们分别尝试下f(1,2)和f(2,1)
假如结果是f(1,2)大于z,f(2,1)小于z?
根据结果,我们可以知道f(2,2)大于max(f(1,2),f(2,1)),因此f(2,2)可以不需要尝试了,我们只需要再次尝试f(3,1)就可以了....
如果f(1,2)和f(2,1)都小于z呢?
那么此时,我们显然需要(1,3),(2,2)和(3,1)都试一试!
我们不妨画图表示一下这两种情况:
如果是第一种情况:
4 x x x x
3 x x x x
2 > x x x
1 < < ! ?
1 2 3 4
如果是第二种情况
4 ? ? ? ?
3 ! ? ? ?
2 < ! ? ?
1 < < ! ?
1 2 3 4
其中"!"代表接下来要计算的f(x,y),而?表示不知道需不需要计算,大于、小于和等于用">","<"和"="表示,"x"表示不需要计算的值。
显然,一个">"或者一个"="会让处于它右上角的部分全部变成"x",而"<"出现的时候,它右边和它上边的值都需要在下一次再次计算(如果没有受到其他">"或者"="的影响。
那么我们的第一个版本的算法也就出现了:——可能比较像动态规划
"""
This is the custom function interface.
You should not implement it, or speculate about its implementation
class CustomFunction:
# Returns f(x, y) for any given positive integers x and y.
# Note that f(x, y) is increasing with respect to both x and y.
# i.e. f(x, y) < f(x + 1, y), f(x, y) < f(x, y + 1)
def f(self, x, y):
"""
class Solution(object):
def findSolution(self, customfunction, z):
"""
:type num: int
:type z: int
:rtype: List[List[int]]
"""
result = []
#初始化一个1000*1000的矩阵(列表),其值全部为"?"
#计算f(1,1),如果f(1,1)>z,那么直接返回一个空列表
#如果f(1,1)=z,直接返回[[1,1]]
#如果f(1,1)<z,将f(1,2)和f(2,1)置为"!"(如果它们不是"x"的话)
#计算所有的矩阵中"!"出现的f(x,y)
#把结果和z进行比较,并将"!"赋值为">","<"或者"="
#如果结果是">",那么将它右边、上边和右上的矩阵值全部置为"x"
#如果结果是"=",把这组解放在结果上,并将它右边、上边和右上的矩阵值全部置为"x"
#如果结果是"<",那么将它右边相邻的元素和上边相邻非"x"的元素置为"!"
#重复上述过程,直到矩阵中没有"?"为止,返回result.
懒得写具体的实现了,所以让ChatGPT生成了一下:
以下是根据你的描述完成的第一个版本的算法。这个算法使用了一个二维矩阵,代表所有可能的 f(x, y)
的计算状态。在计算过程中,通过不断地更新矩阵的值,得到符合条件的解。
class Solution(object):
def findSolution(self, customfunction, z):
"""
:type customfunction: CustomFunction
:type z: int
:rtype: List[List[int]]
"""
result = []
matrix = [['?' for _ in range(1001)] for _ in range(1001)]
# 计算 f(1, 1)
f_1_1 = customfunction.f(1, 1)
if f_1_1 > z:
return result
elif f_1_1 == z:
result.append([1, 1])
return result
else:
matrix[1][1] = '!'
matrix[1][2] = matrix[2][1] = '!'
# 计算所有可能的 f(x, y)
for x in range(1, 1001):
for y in range(1, 1001):
if matrix[x][y] == '!':
f_x_y = customfunction.f(x, y)
if f_x_y == z:
result.append([x, y])
matrix[x][y] = '='
matrix[x+1:][y+1:] = 'x'
elif f_x_y > z:
matrix[x][y] = '>'
matrix[x+1:][y+1:] = 'x'
else:
matrix[x][y] = '<'
matrix[x+1][y] = matrix[x][y+1] = '!'
return result
这个算法对 f(x, y)
进行了一些预处理,然后通过矩阵的更新,找到所有可能的解。尽管这个算法可能不是最优解,但它是一种直观的解法,可以帮助你更好地理解问题。
这个版本的算法显然是可以通过测试的,不过耗时和空间都很大。
2490ms(12.50%)和20.28MB(12.50%)
Current: 48pp(New!)
Overall: 176pp(+41pp)
那么,还有没有其他的方法可以解决这个问题呢
...我之前说过这个题似乎比较像动态规划...但是我们要如何利用这个特性呢?
...
对于这个问题,我们可以考虑使用二分搜索来加速查找。首先,我们观察到 f(x, y)
是递增函数,因此对于给定的 x
,y
,f(x, y)
的值是唯一确定的。
我们可以按照以下步骤进行优化:
- 固定一个变量,比如
x
,然后在范围[1, 1000]
内使用二分搜索查找y
,使得f(x, y) == z
。 - 如果找到了满足条件的
y
,将[x, y]
添加到结果中。 - 增加
x
并重复步骤 1。
这样,我们可以通过二分搜索在 y
的范围内快速找到符合条件的解,从而减小时间复杂度。以下是实现这一优化的代码:
class Solution(object):
def findSolution(self, customfunction, z):
"""
:type customfunction: CustomFunction
:type z: int
:rtype: List[List[int]]
"""
result = []
for x in range(1, 1001):
left, right = 1, 1000
while left <= right:
mid = (left + right) // 2
value = customfunction.f(x, mid)
if value == z:
result.append([x, mid])
break
elif value < z:
left = mid + 1
else:
right = mid - 1
return result
这是Python所写的代码,时间复杂度从O(n^2)
减到了O(nlogn)
,这里ChatGPT利用了对于特定的x来说,使f(x,y)=z的y是唯一的,利用这个特点简化了时间复杂度。
那么,我们能否把这两个特性结合在一起呢?
class Solution(object):
def findSolution(self, customfunction, z):
"""
:type customfunction: CustomFunction
:type z: int
:rtype: List[List[int]]
"""
result = []
x,y,limit_x,limit_y = 1,1,1001,1001 #数据初始化
while x < limit_x or y < limit_y:
left, right = 1, limit_y
if limit_y == 1:
break
while left <= right:
mid = (left + right) // 2
value = customfunction.f(x, mid)
if value == z:
result.append([x, mid])
y = limit_y
break
elif value < z:
left = mid + 1
else:
y = limit_y
right = mid - 1
x += 1
return result
那么这段算法的性能是124ms(25.00%)和13.13MB(12.50%),和之前相比有了显著的提升!
Current: 56pp(+13pp)
Overall: 184pp(+8pp)
不过我为什么感觉还可以优化...我为什么会想到y = limit_y呢?...
后来发现这并不是把它们结合在一起的方法...而是...
class Solution(object):
def findSolution(self, customfunction, z):
"""
:type customfunction: CustomFunction
:type z: int
:rtype: List[List[int]]
"""
result = []
x,y,limit_x,limit_y = 1,1,1001,1001 #数据初始化
while x < limit_x or y < limit_y:
left, right = 1, limit_y
if limit_y == 1:
break
while left <= right:
mid = (left + right) // 2
value = customfunction.f(x, mid)
if value == z:
result.append([x, mid])
y = limit_y #用于快速跳出内层循环
limit_y = mid #这样子才对嘛!~ 动态更新limit_y~(利用x遍历递增的性质,之后的寻找只需要在1,limit_y)范围内执行,并采用二分查找提升效率~
break
elif value < z:
left = mid + 1
else:
y = limit_y
limit_y = mid
right = mid - 1
x += 1
return result
这段代码取得了显著的时间和空间开销的下降!~
16ms(100.00%)和11.59MB(100.00%)
Current: 79pp(+23pp)
Overall: 207pp(+23pp)
Luogu P1013. 进制位
Star Rating: 3.73
题目描述
著名科学家卢斯为了检查学生对进位制的理解,他给出了如下的一张加法表,表中的字母代表数字。 例如:
\[\def\arraystretch{2} \begin{array}{c||c|c|c|c} \rm + & \kern{.5cm} \rm \mathclap{L} \kern{.5cm} & \kern{.5cm} \rm \mathclap{K} \kern{.5cm} & \kern{.5cm} \rm \mathclap{V} \kern{.5cm} & \kern{.5cm} \rm \mathclap{E} \kern{.5cm} \\ \hline\hline \rm L & \rm L & \rm K & \rm V & \rm E \\ \hline \rm K & \rm K & \rm V & \rm E & \rm \mathclap{KL} \\ \hline \rm V & \rm V & \rm E & \rm \mathclap{KL} & \rm \mathclap{KK} \\ \hline \rm E & \rm E & \rm \mathclap{KL} & \rm \mathclap{KK} & \rm \mathclap{KV} \\ \end{array}\]其含义为:
\(L+L=L\),\(L+K=K\),\(L+V=V\),\(L+E=E\)
\(K+L=K\),\(K+K=V\),\(K+V=E\),\(K+E=KL\)
\(\cdots\)
\(E+E=KV\)
根据这些规则可推导出:\(L=0\),\(K=1\),\(V=2\),\(E=3\)。
同时可以确定该表表示的是 \(4\) 进制加法。
输入格式
第一行一个整数 \(n\)(\(3\le n\le9\))表示行数。
以下 \(n\) 行,每行包括 \(n\) 个字符串,每个字符串间用空格隔开。)
若记 \(s_{i,j}\) 表示第 \(i\) 行第 \(j\) 个字符串,数据保证 \(s_{1,1}=\texttt +\),\(s_{i,1}=s_{1,i}\),\(|s_{i,1}|=1\),\(s_{i,1}\ne s_{j,1}\) (\(i\ne j\))。
保证至多有一组解。
输出格式
第一行输出各个字母表示什么数,格式如:L=0 K=1
\(\cdots\) 按给出的字母顺序排序。不同字母必须代表不同数字。
第二行输出加法运算是几进制的。
若不可能组成加法表,则应输出 ERROR!
。
样例 #1
样例输入 #1
5
+ L K V E
L L K V E
K K V E KL
V V E KL KK
E E KL KK KV
样例输出 #1
L=0 K=1 V=2 E=3
4
首先,我们在拿到这个题以后,一定要考虑一下先把数据输入进去
n = int(input())
table = []
for i in range(n):
line = list(map(str, input().split()))
teble.append(line)
然后...看不懂了...
假如说表格是这样的:
+ 0 1
0 0 1
1 1 10
这就是一个简单的二进制加法表
与此同时我们也可以改变0 和 1的顺序:
+ 0 1
1 1 10
0 0 1
这也是一个二进制表格
根据题意可知,\(s_{i,1} \neq s_{j,1}\),所以结果进制数一定大于等于表格大小。(不一定是等于)
如果是三进制表格,那么他会是什么样的呢?
+ 2 1 0
0 2 1 0
1 10 2 1
2 11 10 2
或者说,如果0,1,2不是递增或者递减顺序?
+ 2 0 1
1 10 1 2
0 2 0 1
2 11 2 10
显然,我们已经找到破局的方法了!
我们需要先找到"0"是多少!---因为0一定在和第一行数字相等的一行,以及和第一列数字相等的一列当中,这一行和这一列相交的地方对应的字母就是"0"!
如果找到了"0",接下来要如何去做呢?
无论是几进制,两个一位数相加的结果不可能超过这个进制数字的"10+10".因此,我们可以去找结尾为"0"对应字母的值,然后断定其前边的数字是"1"!(这也就说明,这个表格右下角的子表中的值如果是两位数,不可能超过10+10!)
如果能确定出"1"是多少,那么如何确定其他的值呢?(如果有的话)
找到"1"以后,我们去找"1"对应的行和"1"对应的列,找到"2"对应的字母值,然后就简单了!固定"1"对应的行,找到"2"对应的列的字母,就能找到"3",....直到"n"对应的列为"10"为止结束循环。
(实际上,我们也可以看出来,如果输入的表格长度是n,那它应该是一个n-1进制运算表!)
思路渐渐清晰,我们也来写一下对应的代码!
s = int(input())
table = []
for i in range(s):
line = list(map(str, input().split()))
table.append(line)
dict = {} #存储最后的映射结果
#在table中寻找table[1:][k]与table[1:][0]相等的k和table[l][1:]与table[0][1:]相等的[l],那么table[k][l]对应的字母的映射值为0.
#遍历寻找结尾字符为table[k][l]的长度为2的第一个字符串table[m][n],确定table[m][n][0]对应字母的映射值为1.
#如果进制数为2,即n=3,则返回结果
#否则,找到table[m][n][0]在table[0][1:]对应的位置p和table[m][n][0]在table[1:][0]对应的位置q,找到table[p][q]对应的位置字母的映射值为2
#如果进制数为3,即n=4,则返回结果
#否则,遍历table[1:][p],然后在table[1:][0]中找到table[p][q]对应的位置,确定这个映射值为3,并记录这个值作为下一次在table[1:][0]中寻找的索引,如果还没有确定所有字母,重复上述操作,直到字典长度达到s - 1.
#按照格式输出结果
让ChatGPT生成了一下代码:
n = int(input())
table = []
for i in range(n):
line = list(map(str, input().split()))
table.append(line)
def find_mapping(table, char, n, known_mapping):
for i in range(1, n):
if table[i][0] == char:
return i, 0
if table[0][i] == char:
return 0, i
for i in range(1, n):
for j in range(1, n):
if table[i][j] == char:
if table[i][0] in known_mapping:
return i, 0
else:
return 0, j
def solve(table):
n = len(table)
known_mapping = {} # 存储映射结果
# 寻找0的映射
for i in range(1, n):
for j in range(1, n):
if len(table[i][j]) == 2:
char = table[i][j][1]
known_mapping[char] = 0
break
# 寻找1到n-1的映射
for value in range(1, n):
for i in range(1, n):
for j in range(1, n):
if len(table[i][j]) == 2 and table[i][j][0] == str(value):
char = table[i][j][1]
if char not in known_mapping:
row, col = find_mapping(table, char, n, known_mapping)
known_mapping[char] = value
return known_mapping
def print_result(known_mapping):
result_str = ' '.join([f'{char}={value}' for char, value in sorted(known_mapping.items())])
print(result_str)
def main():
known_mapping = solve(table)
if len(known_mapping) != n - 1:
print('ERROR!')
else:
print_result(known_mapping)
if __name__ == "__main__":
main()
看看能否通过测试吧!
第一次测试发现有三个WE...可能是需要按照表格第一行的顺序输出映射值,字典的顺序可能和表格第一行/第一列顺序不一样...
(因为本题有一个前置条件,行和列出现的顺序是相同的)
..诶?
也许我们可以简化一下代码!
s = int(input())
table = []
for i in range(s):
line = list(map(str, input().split()))
table.append(line)
dict = {} #存储最后的映射结果
#在table中寻找table[1:][k]与table[1:][0]相等的k,那么table[k][k]对应的字母的映射值为0.
#遍历寻找结尾字符为table[k][k]的长度为2的第一个字符串table[m][n],确定table[m][n][0]对应字母的映射值为1.
#如果进制数为2,即n=3,则返回结果
#否则,找到table[m][n][0]在table[0][1:]对应的位置p,找到table[p][p]对应的位置字母的映射值为2
#如果进制数为3,即n=4,则返回结果
#否则,遍历table[1:][p],然后在table[1:][0]中找到table[p][q]对应的位置,确定这个映射值为3,并记录这个值作为下一次在table[1:][0]中寻找的索引,如果还没有确定所有字母,重复上述操作,直到字典长度达到s - 1.
#按照表格中第一行的字母顺序输出结果,并输出进制数
对应的代码如下:
s = int(input())
table = []
for i in range(s):
line = list(map(str, input().split()))
table.append(line)
#print(table)
def solve(table):
n = len(table)
known_mapping = {} # 存储映射结果
# 寻找0的映射
for i in range(1, n):
#print(f"i={i}")
if table[i][1:] == table[0][1:]:
char = table[i][i][0]
known_mapping[char] = 0
break
# 寻找1的映射
for i in range(1,n):
for j in range(1,n):
if len(table[i][j]) == 2:
known_mapping[table[i][j][0]] = 1
break
if len(known_mapping) == 2:
break
if n == 3:
return known_mapping
# 寻找2的映射,初始化一个next_str
next_str = table[i][j][0]
for i in range(1,n):
if table[0][i] == next_str:
known_mapping[table[i][i]] = 2
idx_1 = i
break
if n == 4:
return known_mapping
next_str = table[idx_1][idx_1]
#寻找3到n-2的映射,range的范围是(3,n-1)
for value in range(3, n-1):
for i in range(1, n):
# 在第1行找到"2",..."n-1"对应的索引
if table[0][i] == next_str:
known_mapping[table[idx_1][i]] = value
next_str = table[idx_1][i]
break
return known_mapping
def print_result(known_mapping):
for i in range(1,s):
print(f"{table[0][i]}={known_mapping[table[0][i]]}",end=" ")
print()
print(s - 1)
def main():
known_mapping = solve(table)
#print(known_mapping)
if len(known_mapping) != s - 1:
print('ERROR!')
else:
print_result(known_mapping)
if __name__ == "__main__":
main()
这段代码拿到了80分的分数,不过为什么会有一个过不去的测试用例呢...?
Current: 48pp(New!)
Overall: 246pp(+39pp)
读取的是M,期望E
可能是对某个异常情况没有正确处理吧...
为了进一步验证算法正确性,我们需要对Table全部检查一遍,如果输出有错误,那么也会返回error!
s = int(input())
table = []
for i in range(s):
line = list(map(str, input().split()))
table.append(line)
#print(table)
def solve(table):
n = len(table)
known_mapping = {} # 存储映射结果
# 寻找0的映射
for i in range(1, n):
#print(f"i={i}")
if table[i][1:] == table[0][1:]:
char = table[i][i][0]
known_mapping[char] = 0
break
# 寻找1的映射
for i in range(1,n):
for j in range(1,n):
if len(table[i][j]) == 2:
known_mapping[table[i][j][0]] = 1
break
if len(known_mapping) == 2:
break
if n == 3:
return known_mapping
# 寻找2的映射,初始化一个next_str
next_str = table[i][j][0]
for i in range(1,n):
if table[0][i] == next_str:
known_mapping[table[i][i]] = 2
idx_1 = i
break
if n == 4:
return known_mapping
next_str = table[idx_1][idx_1]
#寻找3到n-2的映射,range的范围是(3,n-1)
for value in range(3, n-1):
for i in range(1, n):
# 在第1行找到"2",..."n-1"对应的索引
if table[0][i] == next_str:
known_mapping[table[idx_1][i]] = value
next_str = table[idx_1][i]
break
return known_mapping
def print_result(known_mapping):
for i in range(1,s):
print(f"{table[0][i]}={known_mapping[table[0][i]]}",end=" ")
print()
print(s - 1)
def cv_int(string,known_mapping):
if len(string) == 1:
return known_mapping[string]
else:
return known_mapping[string[0]] * (s-1) + known_mapping[string[1]]
def main():
known_mapping = solve(table)
#print(known_mapping)
if len(known_mapping) != s - 1:
print('ERROR!')
else:
#检查原来的表格,验证每个值是否成立
int_table = []
for i in range(0,s):
int_table_line = []
for j in range(0,s):
if i == 0 and j == 0:
int_table_line.append(0)
else:
int_table_line.append(cv_int(table[i][j],known_mapping))
int_table.append(int_table_line)
#print(int_table)
valid = True
for i in range(1,s):
for j in range(1,s):
valid = valid and int_table[i][0] + int_table[0][j] == int_table[i][j]
if valid:
print_result(known_mapping)
else:
print('ERROR!')
if __name__ == "__main__":
main()
那么我们也终于通过了这个题目~!
Current:70pp(+22pp)
Overall:267pp(+21pp)
洛谷P1008 三连击
Star Rating:2.64
题目背景
本题为提交答案题,您可以写程序或手算在本机上算出答案后,直接提交答案文本,也可提交答案生成程序。
题目描述
将 \(1, 2, \ldots , 9\) 共 \(9\) 个数分成 \(3\) 组,分别组成 \(3\) 个三位数,且使这 \(3\) 个三位数构成 \(1 : 2 : 3\) 的比例,试求出所有满足条件的 \(3\) 个三位数。
输入格式
无
输出格式
若干行,每行 \(3\) 个数字。按照每行第 \(1\) 个数字升序排列。
样例 #1
样例输入 #1
无
样例输出 #1
192 384 576
* * *
...
* * *
(剩余部分不予展示)
总感觉这道题...应该只是考查我们的理解能力吧..
我大概知道是什么意思了...
感觉有一种很快的方法就是把答案直接列出来,然后print > <
不过...我们要如何才能知道所有的答案呢?
三个数是1:2:3,因此第一个数必须是一个小于333的数字;
要同时出现1~9这9个数字,因此第一个数中必须出现三个不同的数字,而且不能出现0
这样就确定了第一个数一定在123~329之间
基于此方法,我们设计了一个初版的基于穷举的程序把它们都找出来
first_int = 123
while first_int <= 329:
dict = {}
for char in str(first_int):
dict[char] = 1
for char in str(first_int * 2):
dict[char] = 1
for char in str(first_int * 3):
dict[char] = 1
if len(dict) == 9:
print(first_int, first_int * 2, first_int * 3)
first_int += 1
看起来程序输出了正确结果~
192 384 576
219 438 657
267 534 801
273 546 819
327 654 981
一共有5组解!
提交一下试试~不过...发现一个不对的解...因为中间这组把0也映射进去了!
微调了一下!
first_int = 123
while first_int <= 329:
dict = {}
for char in str(first_int):
dict[char] = 1
for char in str(first_int * 2):
dict[char] = 1
for char in str(first_int * 3):
dict[char] = 1
if len(dict) == 9 and '0' not in dict: #如果0不在字典中
print(first_int, first_int * 2, first_int * 3)
first_int += 1
这样的话就只有4组解了!
192 384 576
219 438 657
273 546 819
327 654 981
Yes, Accepted啦~
Current: 24pp(New!)
Overall: 284pp(+14pp)