快速求和
题目背景
2023-10-08 update: 新增两组 hack。
2023-12-16 update: 新增两组 hack。
题目描述
给定一个数字字符串,用最小次数的加法让字符串等于一个给定的目标数字。每次加法就是在字符串的某个位置插入一个加号。在里面要的所有加号都插入后,就像做普通加法那样来求值。
例如,考虑字符串12
,做
0
0
0 次加法,我们得到数字
12
12
12。如果插入
1
1
1 个加号,我们得到
3
3
3,因此,这个例子中,最少用
1
1
1 次加法就得到数字
3
3
3。
再举一例,考虑字符串303
和目标数字
6
6
6,最佳方法不是3+0+3
。而是3+03
。能这样做是因为一个数的前导
0
0
0 不会改变它的大小。
输入格式
第一行:一个字符串 s s s。
第二行:一个整数 n n n。
输出格式
一行一个整数表示最少的加法次数让 s s s 等于 n n n。如果怎么做都不能让 s s s 等于 n n n ,则输出 − 1 -1 −1。
样例 #1
样例输入 #1
99999
45
样例输出 #1
4
提示
数据规模与约定
对于 100 % 100\% 100% 的数据,保证 1 ≤ len ( s ) ≤ 40 1\le \operatorname{len}(s)\le40 1≤len(s)≤40, 1 ≤ n ≤ 1 0 5 1 \leq n\le10^5 1≤n≤105。
分析
令dp[i][j]表示遍历到第i个数,总和为j的最小加号数目,预处理num[i][j]表示字符串i到j所代表的十进制数,两重循环遍历每个区间,令得到的数为tnum,则状态转移为 d p [ i ] [ j ] = m i n ( d p [ i ] [ j ] , d p [ k ] [ j − t n u m ] + 1 ) dp[i][j]=min(dp[i][j],dp[k][j-tnum]+1) dp[i][j]=min(dp[i][j],dp[k][j−tnum]+1),其中i和k为遍历区间的端点
代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 45, M = 1e5 + 5;
int dp[N][M];
int num[N][N];
int n;
int getNum(string s) {
int res = 0;
for(int i = 0; i < s.size(); i ++ ) {
res = res * 10 + s[i] - '0';
}
return res;
}
void init(string s) {
memset(dp, 0x3f, sizeof(dp));
dp[0][0] = 0;
for(int i = 0; i < s.size(); i ++ ) {
for(int j = i; j < s.size(); j ++ ) {
string tmp = s.substr(i, j - i + 1);
num[i + 1][j + 1] = getNum(tmp);
// cout << i << " " << j << " " << num[i][j] << endl;
}
}
}
signed main() {
string s;
cin >> s;
cin >> n;
init(s);
for(int i = 1; i <= s.size(); i ++ ) {
for(int j = 0; j <= n; j ++ ) {
for(int k = i - 1; k >= 0 && num[k + 1][i] <= j; k -- ) {
dp[i][j] = min(dp[i][j], dp[k][j - num[k + 1][i]] + 1);
}
}
}
if(dp[s.size()][n] >= 45) cout << -1;
else cout << dp[s.size()][n] - 1 << endl;
return 0;
}
编辑距离
题目描述
设 A A A 和 B B B 是两个字符串。我们要用最少的字符操作次数,将字符串 A A A 转换为字符串 B B B。这里所说的字符操作共有三种:
- 删除一个字符;
- 插入一个字符;
- 将一个字符改为另一个字符。
A , B A, B A,B 均只包含小写字母。
输入格式
第一行为字符串 A A A;第二行为字符串 B B B;字符串 A , B A, B A,B 的长度均小于 2000 2000 2000。
输出格式
只有一个正整数,为最少字符操作次数。
样例 #1
样例输入 #1
sfdqxbw
gfdgw
样例输出 #1
4
提示
对于 100 % 100 \% 100% 的数据, 1 ≤ ∣ A ∣ , ∣ B ∣ ≤ 2000 1 \le |A|, |B| \le 2000 1≤∣A∣,∣B∣≤2000。
分析
令 d p [ i ] [ j ] dp[i][j] dp[i][j]为 a a a的前 i i i个字符变成 b b b的前 j j j个字符所需要的最小操作,若 a [ i ] = = b [ j ] a[i]==b[j] a[i]==b[j],此时次数可以不变,即 d p [ i ] [ j ] = d p [ i − 1 ] [ j − 1 ] dp[i][j]=dp[i-1][j-1] dp[i][j]=dp[i−1][j−1],也可以是 a a a添加一个字符,此时需要 a a a的前 i i i个字符和 b b b的前 j − 1 j-1 j−1个字符相同,即 d p [ i ] [ j ] = d p [ i ] [ j − 1 ] + 1 dp[i][j]=dp[i][j-1]+1 dp[i][j]=dp[i][j−1]+1,也可以是a删除一个字符,此时需要a的前i-1个字符和b的前j个字符相同,即 d p [ i ] [ j ] = d p [ i − 1 ] [ j ] + 1 dp[i][j]=dp[i-1][j]+1 dp[i][j]=dp[i−1][j]+1,若 a [ i ] ! = b [ j ] a[i]!=b[j] a[i]!=b[j],此时添加和删除的情况不变,还可以进行修改,因此这时候 d p [ i ] [ j ] = d p [ i − 1 ] [ j − 1 ] + 1 dp[i][j]=dp[i-1][j-1]+1 dp[i][j]=dp[i−1][j−1]+1,最后这三种情况取最小即可,注意初始化, d p [ 0 ] [ i ] = i dp[0][i]=i dp[0][i]=i因为只能进行添加操作,同理 d p [ i ] [ 0 ] = i dp[i][0]=i dp[i][0]=i
代码
#include<bits/stdc++.h>
using namespace std;
const int N = 2005;
int dp[N][N];
int main() {
string a, b;
cin >> a >> b;
int n = a.size(), m = b.size();
for(int i = 0; i <= m; i ++ ) dp[0][i] = i;
for(int i = 0; i <= n; i ++ ) dp[i][0] = i;
for(int i = 1; i <= n; i ++ ) {
for(int j = 1; j <= m; j ++ ) {
if(a[i - 1] == b[j - 1]) dp[i][j] = min(min(dp[i - 1][j] + 1, dp[i][j - 1] + 1), dp[i - 1][j - 1]);
else dp[i][j] = min(min(dp[i - 1][j] + 1, dp[i][j - 1] + 1), dp[i - 1][j - 1] + 1);
}
}
cout << dp[n][m] << endl;
return 0;
}