安吉D3-T4
- 1安吉D19 T1
- 2安吉D19 T3
- 3安吉D20 模考12总结
- 4安吉D17 T3
- 5安吉D17 T4
- 6安吉D19 模考11总结
- 7安吉D15 A
- 8安吉D16 T4
- 9安吉D17 T1
- 10安吉D17 模考10总结
- 11安吉D16 T3
- 12安吉D16 模考9总结
- 13安吉D13 模考8总结
- 14安吉D13-T2
- 15安吉D13-T3
- 16安吉D12-T3
- 17安吉D11-B
- 18安吉D11-C
- 19安吉D11-D
- 20安吉D12 模考7总结
- 21安吉D12-T2
- 22安吉D11-A
- 23安吉D10 模考6总结
- 24安吉D10-T3
- 25安吉D10-T4
- 26安吉D8-E
- 27安吉D9-T4
- 28安吉D8-A
- 29安吉D8-B
- 30安吉D8-C
- 31安吉D8-D
- 32安吉D9 模考5总结
- 33安吉D9-T2
- 34安吉D9-T3
- 35安吉D6-T4
- 36安吉D4-G
- 37安吉D5-T2
- 38安吉D6 模考4总结
- 39安吉D6-T1
- 40安吉D6-T3
- 41安吉-开营测试 T4
- 42安吉D5 模考3总结
- 43安吉D5-T3
- 44安吉D4-A
- 45安吉D4-B
- 46安吉D4-C
- 47安吉D4:容斥原理
- 48安吉Day4-D
- 49安吉D3 模考2总结
- 50安吉D3-T1
- 51安吉D3-T2
- 52安吉D3-T4本文
- 53安吉D2 模考1总结
- 54安吉D2-T2
- 55安吉D1-G
- 56安吉D1-L
- 57安吉D2-T4
原题呈现
题目描述
给定两个长度均为 的字符串 ,它们都只包含小写英文字母.
另外给定 条转换规则,第 条规则由三个量组成:
- 字符串
- 字符串
- 代价
并且保证字符串 与 的长度相同, 是正整数.
一次操作中,你可以选择当前字符串中的一个区间 ,若该区间对应的子串恰好等于某个 ,则可以花费 的代价,将这一段替换为 .
你可以进行任意多次操作,但所有操作所选择的区间必须满足下面条件之一:
- 两次操作所选区间完全相同;
- 两次操作所选区间没有公共位置.
也就是说,若两次操作作用在不同区间上,则这两个区间不能有任何重叠;若有重叠,则它们必须是同一个区间.
请你求出将字符串 变为字符串 的最小总代价.若无法完成转换,输出 .
输入格式
第一行一个字符串 . 第二行一个字符串 . 第三行一个整数 ,表示转换规则的条数. 接下来 行,每行输入两个字符串 和一个整数 ,表示一条规则.
输出格式
输出一个整数,表示将 转换为 的最小总代价.如果无解,输出 .
样例
样例 1
输入
abcdacbe6a b 2b c 5c b 5c e 1e b 2d e 20输出
28样例 2
输入
abcdefghacdeeghh3bcd cde 1fgh thh 3thh ghh 5输出
9样例 3
输入
abcdefghaddddddd2bcd ddd 100defgh ddddd 1578输出
-1样例解释
样例 1
一种最优方案如下:
- 把位置 上的
b变成c,代价 ; - 把位置 上的
c变成e,代价 ; - 再把位置 上的
e变成b,代价 ; - 把位置 上的
d变成e,代价 .
总代价为 . 注意第三步与第二步作用在同一个区间,因此是允许的.
样例 2
可以按如下方式转换:
- 将区间 的
bcd变为cde,代价 ; - 将区间 的
fgh变为thh,代价 ; - 再将区间 的
thh变为ghh,代价 .
前一次和后两次操作分别作用在两个互不相交的区间上,因此合法;后两次操作作用在同一个区间上,也合法.
总代价为 .这也是最小代价.
样例 3
若先把区间 的 bcd 变成 ddd,那么之后若想再处理区间 ,这两个区间会在位置 发生重叠,但它们并不是同一个区间,因此不合法.
反过来,若先处理区间 ,再处理区间 ,同样会产生非法重叠.
因此无论怎样都无法得到目标串,答案为 .
数据范围
对于所有数据,满足:
- 字符串仅由小写字母组成.
| 测试点编号 | 特殊限制 | ||
|---|---|---|---|
| 1,2 | - | ||
| 3,4,5 | 所有规则长度为 | ||
| 6,7,8 | 所有规则长度 | ||
| 9,10 | 所有规则长度为 | ||
| 11~14 | 所有规则长度 | ||
| 15~20 | - |
观察所谓的转换规则:给定一个起始字符串,一个终点字符串,和一个将起始字符串转换为终点字符串的权值,可以将其想象成一条单项边,使用哈希将两个字符串离散成两个数字,并使用 map 映射到点的编号.
因此,对于所有的转换规则,可以将其看作是一张图,只需要在图上跑一边 floyd,就可以在 得知两个字符串之间的权值,floyd 时间复杂度 .
紧接着,观察这个 的范围,可以使用 dp,定义 dp[i] 表示 和 的前 个字符相等(即:s[0..i-1] = t[0..i-1])所需要的代价,有初始状态:
dp[0] = 0;有转移方程:
其中, 指通过一次变换操作将 变为 所需要的代价,可以先预处理出 和 的哈希,再将两者进行子串哈希为数字 和 ,复杂度 ,再通过 map 找到点的编号 和 ,最后查询 floyd 得到的 dis 数组,.当然,如果查询不到 和 ( 或 为 0),或者说 到 没有连边,这个转移方程就不成立.
最终状态:dp[s.size()].
你会发现,这个 dp 是 的,怎么优化呢?不难发现,一共只有 条规则,每条规则只有一个起始字符串,也就是说最多只有 个起始字符串,也就是说,这些字符串的长度最多只有 种,因此,在上述转换方程中, 不需要从 枚举到 ,只需要遍历所有字符串长度组成的集合 len_set(举个例子,看测试点 3~5,所有的起始字符串长度都是 1,因此 len_set 中只有 1,len 也只需要取到 1,取到别的是浪费时间的操作),时间复杂度 .
看着不错,交一发试试.恭喜你,被卡常了.
这里有一些卡常小技巧 :
- 能不用
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-basedint 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 离散化转换为数字问题.
文章分享
如果这篇文章对你有帮助,欢迎分享给更多人!


