1. 问题描述:
给定一个 n 个点 m 条边的有向图。图中可能包含重边和自环,也可能不连通。给每个点分配一个小写字母。我们定义一条路径的权值为出现频率最高的字母的出现次数。例如,如果一条路径上的字母是 abaca,则该路径的权值为 3。请你找到给定图中权值最大的路径,输出这个最大路径权值。
输入格式
第一行包含两个整数 n 和 m。第二行包含一个由小写字母构成的字符串 s,其中第 i 个字母表示第 i 个点上的字母。接下来 m 行,每行包含两个整数 x,y 表示存在一条从点 x 到点 y 的边。所有点的编号为 1∼n。
输出格式
输出最大路径权值。如果这个权值是无穷大,则输出 −1。
数据范围
前三个测试点满足 1 ≤ n,m ≤ 10。
所有测试点满足 1 ≤ n,m ≤ 3 × 10 ^ 5,1 ≤ x,y ≤ n。
输入样例1:
5 4
abaca
1 2
1 3
3 4
4 5
输出样例1:
3
输入样例2:
6 6
xzyabc
1 2
3 1
2 3
5 4
4 3
6 4
输出样例2:
-1
来源:https://www.acwing.com/problem/content/description/3816/
2. 思路分析:
分析题目可以知道当有向图中存在环的时候此时环中每一个字母可以出现无限次,所以答案为-1,判断图中是否存在环其中有好几种常见的方法,一般来说使用拓扑排序来判断有向图中是否存在环会比较简单一点,代码也比较容易编写,而且可以发现我们使用拓扑排序判断有向图中是否存在环的时候如果图中不存在环说明是有解的,此时我们可以得到拓扑排序的序列,因为需要求解路径中的最大权值,最大权值肯定对应其中一个小写字母而总共有26个小写字母所以我们可以枚举26个小写字母判断当前枚举的字母是否可以求解出更大的权值,对于图中所有节点,如果等于当前枚举的字母那么权值为1,否则为0;所以问题就转化为了在权值不是0和1的拓扑图上求解最长路径,因为数据规模为3 * 10 ^ 5,所以不能够使用dijkstra算法或者spfa算法求解最长路径,我们知道dp求解最值本质上是在拓扑图上求解最长/短路径(只有状态之间不存在依赖关系的时候才可以按顺序递推求解dp数组值),所以我们可以使用递推来求解有向图中的最长路径,由于使用了拓扑排序得到了拓扑排序的序列所以我们可以使用拓扑序列逆序递推求解最长路径即可,逆序递推最终肯定可以求解出从某个节点出发的最大权值。
3. 代码如下:
import collections
from typing import List
class Solution:
# 判断拓扑排序是否有解如果有解q就是拓扑排序序列
def topsort(self, n: int, d: List[int], q: List[int], g: List[List[int]]):
queue = collections.deque()
count = 0
for i in range(1, n + 1):
if d[i] == 0:
queue.append(i)
while queue:
p = queue.popleft()
count += 1
# 当前入度为0的点就是拓扑排序的节点
q.append(p)
for next in g[p]:
d[next] -= 1
if d[next] == 0:
queue.append(next)
# 只有入队次数等于节点数目说明才有拓扑序
return count == n
def process(self):
n, m = map(int, input().split())
# s前面加一个字母"a"这样s元素的下标可以从1开始
s = "a" + input()
g = [list() for i in range(n + 10)]
d = [0] * (n + 10)
for i in range(m):
x, y = map(int, input().split())
g[x].append(y)
d[y] += 1
# q存储的就是拓扑排序的序列
q = list()
if not self.topsort(n, d, q, g):
return -1
f = [0] * (n + 10)
res = 0
# 枚举26个字母, 判断当前枚举的字母是否是可以求解到路径最大权值
for i in range(26):
c = chr(i + 97)
n = len(q)
# 从拓扑序列最后一个数字开始逆序递推
for j in range(n - 1, -1, -1):
t = q[j]
v = 1 if s[t] == c else 0
f[t] = v
for next in g[q[j]]:
f[t] = max(f[t], f[next] + v)
res = max(res, f[t])
return res
if __name__ == '__main__':
print(Solution().process())