牛客春招刷题训练营 - 3.17题解 | C++

活动地址: 牛客春招刷题训练营 - 编程打卡活动

简单题:数字颠倒

读入一个字符串,倒序输出就行了。

#include <iostream>
using namespace std;

int main() {
    string s;
    cin >> s;
    int n = s.size();
    for(int i=n-1; i>=0; i--)
        cout << s[i];
    return 0;
}

中等题:密码截取

这道题本质就是求一个字符串的最长回文字串。

首先注意到题目范围是2500,那么暴力的O(N^3)做法(枚举所有字串然后判断是不是回文)就肯定不行了。

所以我们要找到一个O(N^2)以内的做法。

比较容易想到的一个做法是枚举回文中心,然后向外扩散到最长的回文子串,不断更新最大长度的值就好。

然后关于回文中心的选取其实一共有两种情况,一种情况中心是字母,对应奇数长度的字串;另一种中心是空,对应偶数长度的字串。我下面的代码就用一个循环,通过模2的奇偶性把两种位置一起枚举了。

#include <iostream>
#include <vector>
using namespace std;

int main() {
    string s;
    cin >> s;
    int n = s.size();
    int res = 0;
    for(int i=1; i<(n-1)*2; i++) {
        if(i % 2 == 0) {
            int j = i / 2;
            int tot = 1;
            for(int l=j-1, r=j+1; l>=0 && r < n; l--, r++) {
                if(s[l] == s[r]) tot += 2;
                else break;
            }
            res = max(res, tot);
        } else {
            int j = i / 2;
            int tot = 0;
            for(int l=j, r=j+1; l>=0 && r < n; l--, r++) {
                if(s[l] == s[r]) tot += 2;
                else break;
            }
            res = max(res, tot);
        }
    }
    cout << res << endl;
    return 0;
}

当然,这道题有个复杂度更低的算法,也就是马拉车算法。在算法竞赛里一般都会用这种写法,因为复杂度是 O(N)。

想要了解的小伙伴们可以自行学习一下,这里就不赘述了。下面是笔者的ACM板子,大家可以参考一下。

#include <iostream>
#include <vector>
using namespace std;
string Get_new(string &str) {
    string temp = "#";
    for (int i = 0; str[i]; ++i) {
        (temp += str[i]) += "#";
    }
    return temp;
}
vector<int> manacher(string &str) {
    vector<int> r(str.size()+5);
    int c = 0;
    for (int i = 1; str[i]; ++i) {
        if (c + r[c] > i)  r[i] = min(r[2 * c - i], c + r[c] - i);
        while (i - r[i] >= 0 && str[i - r[i]] == str[i + r[i]])  ++r[i];
        --r[i];
        if (i + r[i] > c + r[c])  c = i;
    }
    return r;
}
int main() {
    string s;
    cin >> s;
    s = Get_new(s);
    auto d = manacher(s);
    int res = 0;
    // d数组的最大值就是最长回文字串
    for(int x: d)
        res = max(res, x);
    cout << res << endl;
    return 0;
}

困难题:计算字符串的编辑距离

DP经典问题,编辑距离。

解释一下DP数组的含义吧,dp[i][j]就是代表a[0...i-1] (a的前i个字母组成的字符串) 和 b[0...j-1] (b的前j个字母组成的字符串) 之间的编辑距离。那么dp[n][m]其实就是我们要求的答案。

怎么转移呢?

不难想到,假如说 a[i-1] == b[j-1](ps:a[i-1]是a串的第i位),dp[i][j]直接等于dp[i-1][j-1]就行了,因为这一对字母加上之后不需要耗费编辑次数。

但如果不相等,就对应了五种编辑的可能,把a[i-1]删除、把b[j-1]删除、把a[j-1]和b[j-1]改成一样的、a[i-2]之后插入b[j-1]或在b[i-2]之后插入a[i-1]。

再仔细想一下,其实插入和删除本质上是一样的。那我们不如就只做删除操作。

两种删除的情况分别对应dp[i-1][j]+1和dp[i][j-1]+1,而更改操作则对应dp[i-1][j-1]+1,把这三种情况的值取一个最小值就行了。

dp一遍后,输出dp[n][m]。

#include <iostream>
#include <vector>
using namespace std;

int main() {
    string a, b;
    cin >> a >> b;
    int n = a.size();
    int m = b.size();
    vector<vector<int>> dp(n+1, vector<int>(m+1));
    for(int i=0; i<=n; i++)
        dp[i][0] = i;
    for(int j=0; j<=m; j++)
        dp[0][j] = j;
    for(int i=1; i<=n; i++) {
        for(int j=1; j<=m; j++) {
            if(a[i-1] == b[j-1]) dp[i][j] = dp[i-1][j-1];
            else {
                dp[i][j] = min(dp[i-1][j], dp[i][j-1]) + 1;
                dp[i][j] = min(dp[i][j], dp[i-1][j-1] + 1);
            }
        }
    }
    cout << dp[n][m] << endl;
    return 0;
}

#牛客春招刷题训练营#
全部评论

相关推荐

评论
1
收藏
分享

创作者周榜

更多
牛客网
牛客企业服务