视频加载失败

安吉D3-T4

1930 字
10 分钟
安吉D3-T4
原题呈现

题目描述#

给定两个长度均为 nn 的字符串 S,TS, T,它们都只包含小写英文字母.

另外给定 mm 条转换规则,第 ii 条规则由三个量组成:

  • 字符串 AiA_i
  • 字符串 BiB_i
  • 代价 CiC_i

并且保证字符串 AiA_iBiB_i 的长度相同,CiC_i 是正整数.

一次操作中,你可以选择当前字符串中的一个区间 [l,r][l, r],若该区间对应的子串恰好等于某个 AiA_i,则可以花费 CiC_i 的代价,将这一段替换为 BiB_i

你可以进行任意多次操作,但所有操作所选择的区间必须满足下面条件之一:

  1. 两次操作所选区间完全相同;
  2. 两次操作所选区间没有公共位置.

也就是说,若两次操作作用在不同区间上,则这两个区间不能有任何重叠;若有重叠,则它们必须是同一个区间.

请你求出将字符串 SS 变为字符串 TT 的最小总代价.若无法完成转换,输出 1-1


输入格式#

第一行一个字符串 SS. 第二行一个字符串 TT. 第三行一个整数 mm,表示转换规则的条数. 接下来 mm 行,每行输入两个字符串 Ai,BiA_i, B_i 和一个整数 CiC_i,表示一条规则.


输出格式#

输出一个整数,表示将 SS 转换为 TT 的最小总代价.如果无解,输出 1-1


样例#

样例 1#

输入

abcd
acbe
6
a b 2
b c 5
c b 5
c e 1
e b 2
d e 20

输出

28

样例 2#

输入

abcdefgh
acdeeghh
3
bcd cde 1
fgh thh 3
thh ghh 5

输出

9

样例 3#

输入

abcdefgh
addddddd
2
bcd ddd 100
defgh ddddd 1578

输出

-1

样例解释#

样例 1#

一种最优方案如下:

  1. 把位置 22 上的 b 变成 c,代价 55
  2. 把位置 33 上的 c 变成 e,代价 11
  3. 再把位置 33 上的 e 变成 b,代价 22
  4. 把位置 44 上的 d 变成 e,代价 2020

总代价为 5+1+2+20=285 + 1 + 2 + 20 = 28. 注意第三步与第二步作用在同一个区间,因此是允许的.

样例 2#

可以按如下方式转换:

  1. 将区间 [2,4][2,4]bcd 变为 cde,代价 11
  2. 将区间 [6,8][6,8]fgh 变为 thh,代价 33
  3. 再将区间 [6,8][6,8]thh 变为 ghh,代价 55

前一次和后两次操作分别作用在两个互不相交的区间上,因此合法;后两次操作作用在同一个区间上,也合法.

总代价为 1+3+5=91 + 3 + 5 = 9.这也是最小代价.

样例 3#

若先把区间 [2,4][2,4]bcd 变成 ddd,那么之后若想再处理区间 [4,8][4,8],这两个区间会在位置 44 发生重叠,但它们并不是同一个区间,因此不合法. 反过来,若先处理区间 [4,8][4,8],再处理区间 [2,4][2,4],同样会产生非法重叠. 因此无论怎样都无法得到目标串,答案为 1-1


数据范围#

对于所有数据,满足:

  • 1n500001 \le n \le 50000
  • 1m1001 \le m \le 100
  • 1Ai=Bin1 \le |A_i| = |B_i| \le n
  • 1Ci1051 \le C_i \le 10^5
  • 字符串仅由小写字母组成.
测试点编号nn \lemm \le特殊限制
1,210101010-
3,4,510001000100100所有规则长度为 11
6,7,810001000100100所有规则长度 50\le 50
9,105000050000100100所有规则长度为 11
11~145000050000100100所有规则长度 50\le 50
15~205000050000100100-

观察所谓的转换规则:给定一个起始字符串,一个终点字符串,和一个将起始字符串转换为终点字符串的权值,可以将其想象成一条单项边,使用哈希将两个字符串离散成两个数字,并使用 map 映射到点的编号.

因此,对于所有的转换规则,可以将其看作是一张图,只需要在图上跑一边 floyd,就可以在 O(1)O(1) 得知两个字符串之间的权值,floyd 时间复杂度 O(m3)O(m^3)

紧接着,观察这个 nn 的范围,可以使用 dp,定义 dp[i] 表示 sstt 的前 ii 个字符相等(即:s[0..i-1] = t[0..i-1])所需要的代价,有初始状态:

dp[0] = 0;

有转移方程:

dp[i]=minlen=1idp[ilen]+cost(ilen+1,i)dp[i] = \min^{i}_{len=1} dp[i-len] + cost(i-len+1, i)

其中,cost(l,r)cost(l,r)通过一次变换操作s[l..r]s[l..r] 变为 t[l..r]t[l..r] 所需要的代价,可以先预处理出 sstt 的哈希,再将两者进行子串哈希为数字 hlhlhrhr,复杂度 O(1)O(1),再通过 map 找到点的编号 aabb,最后查询 floyd 得到的 dis 数组,cost(l,r)=dis[a][b]cost(l,r)=dis[a][b].当然,如果查询不到 aabbmap[hl]map[hl]map[lr]map[lr] 为 0),或者说 aabb 没有连边,这个转移方程就不成立.

最终状态:dp[s.size()]

你会发现,这个 dp 是 O(n2)O(n^2) 的,怎么优化呢?不难发现,一共只有 mm 条规则,每条规则只有一个起始字符串,也就是说最多只有 mm 个起始字符串,也就是说,这些字符串的长度最多只有 mm 种,因此,在上述转换方程中,lenlen 不需要从 11 枚举到 ii,只需要遍历所有字符串长度组成的集合 len_set(举个例子,看测试点 3~5,所有的起始字符串长度都是 1,因此 len_set 中只有 1,len 也只需要取到 1,取到别的是浪费时间的操作),时间复杂度 O(nm)O(nm)

看着不错,交一发试试.恭喜你,被卡常了.

这里有一些卡常小技巧 :

  • 能不用 long long 就不用.
  • 使用快读快写.
  • 少用 STL 的迭代器,对于 set 和 map 可以转成 vector 之后使用下标读写.
  • 减少 vector 申请空间的次数.

标程

#include <bits/stdc++.h>
using namespace std;
const int BASE = 128;
const int N = 50005;
const int M = 2 * 105;
const int MOD = 1e9 + 7;
const int INF = 0x3f3f3f3f;
string s, t;
int m, dis[M][M], cnt;
map<int, int> g;
set<int> len_set;
int dp[N]; // dp[i] 表示前 i 个字符相等的代价
int sh[N], th[N];
int p[N];
// 哈希函数,针对字符串
int hs(string &str) {
int h = 0;
for (int i = 1; i <= str.size(); i++) {
(h = 1ll * h * BASE % MOD + str[i - 1]) %= MOD;
}
return h % MOD;
}
// 哈希函数,针对子串
int hs(int *h, int l, int r) {
return (h[r] - (1ll * h[l - 1] * p[r - l + 1] % MOD) + MOD) % MOD;
}
// 计算[l,r]的花费,1-based
int cost(int l, int r) {
int a = g[hs(sh, l, r)];
int b = g[hs(th, l, r)];
if (a == 0 || b == 0)
return INF;
return dis[a][b];
}
signed main() {
freopen("string.in", "r", stdin);
freopen("string.out", "w", stdout);
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> s >> t >> m;
memset(dis, 0x3f, sizeof(dis));
// 预处理哈希
p[0] = 1;
for (int i = 1; i <= s.size(); i++) {
sh[i] = (1ll * sh[i - 1] * BASE + s[i - 1]) % MOD;
}
for (int i = 1; i <= t.size(); i++) {
th[i] = (1ll * th[i - 1] * BASE + t[i - 1]) % MOD;
p[i] = 1ll * p[i - 1] * BASE % MOD;
}
// 建图
for (int i = 1; i <= m; i++) {
string a, b;
int c;
cin >> a >> b >> c;
len_set.insert(a.size());
int ha = hs(a), hb = hs(b);
if (g[ha] == 0)
g[ha] = ++cnt;
if (g[hb] == 0)
g[hb] = ++cnt;
int x = g[ha], y = g[hb];
dis[x][y] = min(dis[x][y], c);
}
// floyd
for (int i = 1; i <= cnt; i++) {
dis[i][i] = 0;
}
for (int k = 1; k <= cnt; k++) {
for (int i = 1; i <= cnt; i++) {
for (int j = 1; j <= cnt; j++) {
dis[i][j] = min(dis[i][j], dis[i][k] + dis[k][j]);
}
}
}
vector<int> len;
for (auto i : len_set)
len.push_back(i);
// 开始 DP
memset(dp, 0x3f, sizeof(dp));
dp[0] = 0;
for (int i = 1; i <= s.size(); i++) {
if (s[i - 1] == t[i - 1]) {
dp[i] = min(dp[i], dp[i - 1]);
}
for (int j : len) {
if (i - j >= 0) {
int c = cost(i - j + 1, i);
if (c >= INF)
continue;
dp[i] = min(dp[i], dp[i - j] + c);
}
}
}
if (dp[s.size()] < INF)
cout << dp[s.size()];
else {
cout << -1;
}
}

字符串问题可以通过哈希 + map 离散化转换为数字问题.

文章分享

如果这篇文章对你有帮助,欢迎分享给更多人!

安吉D3-T4
https://blog.jerrylab.top/posts/problem/anji2026/D3/T4/
作者
Jerry
发布于
2026-08-03
许可协议
CC BY-NC-SA 4.0
Profile Image of the Author
Jerry
Hello, I'm Jerry.
公告
欢迎来到我的博客!这是一则示例公告。
分类
标签
最新动态
站点统计
文章
80
分类
3
标签
35
总字数
89,198
运行时长
0
最后活动
0 天前
站点信息
构建平台
Cloudflare Pages
博客版本
Firefly v6.16.5
文章许可
CC BY-NC-SA 4.0