CF919D Substring 题解
传送门: ⊗ \otimes ⊗ ⊕ \oplus ⊕
题目大意
给定一个
n
n
n 点
m
m
m 边的有向图,每个顶点代表一个小写字母。定义一条路径的权值为出现最多次数的字母的出现次数。例如一条路径上的字母分别是 abaca
,那么这条路径的权值就是
3
3
3。你的任务是找到这张图上最大的路径权值。(有环则输出 -1
.)
样例输入
5 4
abaca
1 2
1 3
3 4
4 5
样例输出
3
解析
首先考虑 dfs/dp,在合并信息的过程中发现“最多出现的字母”是不可加的,也就是说不能只记录最多出现的字母。于是便想到了用
f
i
,
j
f_{i,j}
fi,j 记录以
i
i
i 为结尾的路径中,字母
j
j
j 最多出现的次数。在这里,我们需要将小写字母量化,常用方法是 str[i] = s[i - 1] - 'a'
,即 ASCII 码相减。
再看题目中,有向图+环+路径,自然让人联想到了拓扑排序,核心思想是用一个节点的所有前驱更新这个节点的信息。将拓扑排序作为转移顺序,也就是拓扑排序的同时完成转移。对于一条边
(
u
,
v
)
(u,v)
(u,v),先将
u
u
u 的信息合并到
v
v
v 上,再考虑这条边产生的贡献。即对于
∀
i
,
f
v
,
i
=
max
(
f
v
,
i
,
f
u
,
i
)
\forall i,f_{v,i}=\max(f_{v,i},f_{u,i})
∀i,fv,i=max(fv,i,fu,i),再把
v
v
v 对应的小写字母
s
t
r
v
str_v
strv 用
f
u
,
s
t
r
v
+
1
f_{u,str_v}+1
fu,strv+1 更新最大值。
最后考虑一下环的情况。拓扑排序可以判环,记录入队的元素个数
c
n
t
cnt
cnt,若
c
n
t
≠
n
cnt\not=n
cnt=n(严格来讲是
c
n
t
<
n
cnt<n
cnt<n)则说明存在环,反之不存在。
代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
inline int read(){
char ch;
int s = 0, w = 1;
for(; ch < '0' || ch > '9'; w *= ch == '-' ? -1 : 1, ch = getchar());
for(; ch >= '0' && ch <=' 9'; s = s * 10 + ch - '0', ch = getchar());
return s * w;
}
const int MAXN = 300005;
const int MAXM = 300005;
int N, M, f[MAXN][30], str[MAXN], indeg[MAXN];
struct Graph{
struct Edge{
int to, nxt;
} e[MAXM << 1];
int head[MAXN], tot;
void add(int u, int v){
e[++tot].to = v;
e[tot].nxt = head[u];
head[u] = tot;
}
} G;
signed main(){
cin >> N >> M;
string s;
cin >> s;
for(int i = 1; i <= N; i++){
str[i] = s[i - 1] - 'a';
f[i][str[i]]++;
}
for(int i = 1, u, v; i <= M; i++){
u = read(), v = read();
indeg[v]++;
G.add(u, v);
}
queue<int> q;
int cnt = 0;
for(int i = 1; i <= N; i++){
if(indeg[i] == 0){
q.push(i);
cnt++;
}
}
while(!q.empty()){
int u = q.front();
q.pop();
for(int i = G.head[u], v; i; i = G.e[i].nxt){
v = G.e[i].to;
indeg[v]--;
if(indeg[v] == 0) {
q.push(v);
cnt++;
}
for(int j = 0; j < 26; j++){
f[v][j] = max(f[v][j], f[u][j]);
}
f[v][str[v]] = max(f[v][str[v]], f[u][str[v]] + 1);
}
}
if(cnt != N){
cout << -1 << endl;
return 0;
}
int res = 0;
for(int i = 1; i <= N; i++){
for(int j = 0; j < 26; j++){
res = max(res, f[i][j]);
}
}
cout << res << endl;
return 0;
}