Codeforces 335B Palindrome 鸽巢原理 + DP

题目大意:

就是现在给出一个长度不超过5*10^4的串, 求其最长回文串, 如果回文串长度超过100只需要输出长度为100的即可, 输入的字符串只包含26种小写英文字母

另外就是这个题目中的回文串并不要求在原来的串当中是连续出现的, 只需要出现的位置顺序一样即可, 并不要求连续


大致思路:

做练习的时候一眼看标题课描述没看Hint就脑补直接当常见的的连续的回文串来做了....上来就敲了一发后缀数组结果发现样例没过然后看错题了啊....

现在重新做了一下, 发现是一个可以二维复杂度O(n*n)的dp, 因为最多只有26个字母, 当字符串长度超过26*99 + 1时一定会有某个字符出现了100次以上, 输出这个字符100次即可

所以只需要对于长度不超过大约2600一下的字符串进行dp求解即可

DP状态转移方程和路径回溯见代码


代码如下:

Result  :  Accepted     Memory  :  79432 KB     Time  :  186 ms

/*
 * Author: Gatevin
 * Created Time:  2015/3/3 17:45:08
 * File Name: Shana.cpp
 */
#include<iostream>
#include<sstream>
#include<fstream>
#include<vector>
#include<list>
#include<deque>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<bitset>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cctype>
#include<cmath>
#include<ctime>
#include<iomanip>
using namespace std;
const double eps(1e-8);
typedef long long lint;

/*
 * 用dp[i][j]表示位置i和j之间的串能找到的最长的回文串(该题定义的版本)的长度
 * 那么不难发现转移方程当s[i] == s[j]时 dp[i][j] = dp[i + 1][j - 1] + 2 (Greedy)
 * 否则dp[i][j] = max(dp[i + 1][j], dp[i][j - 1])
 * 然后我们用一个last[l][r]表示dp[l][r]的来源上一个状态是dp[last[l][r].first][last[l][r].second]
 * 用last数组回溯就可以找到原来的串, 注意回溯的时候L和R变化至s[L] == s[R] && R == L + 1的时候回文串是偶数长度
 * 这个dp是O(n*n)的
 * 但是由于本题只有26个字母, 当长度达到25*99 + 1时就必然会出现某个字符出现了至少100次(鸽巢原理)
 * 所以只需要对长度 < 2600的进行dp求解就足够了
 */

int dp[2600][2600];
pair<int, int> last[2600][2600];
int n;
char s[50010];
int cnt[30];
vector<char> v;

int main()
{
    scanf("%s", s);
    n = strlen(s);
    if(n >= 2600)
    {
        for(int i = 0; i < n; i++)
        {
            cnt[s[i] - 'a']++;
            if(cnt[s[i] - 'a'] >= 100)
            {
                for(int j = 0; j < 100; j++)
                    printf("%c", s[i]);
                return 0;
            }
        }
    }
    for(int i = 0; i < n; i++) dp[i][i] = 1;
    for(int L = 2; L <= n; L++)
        for(int i = 0; i + L - 1 < n; i++)
        {
            if(s[i] == s[i + L - 1])
            {
                dp[i][i + L - 1] = dp[i + 1][i + L - 2] + 2;
                last[i][i + L - 1] = make_pair(i + 1, i + L - 2);//记录最佳状态的来源
            }
            else
            {
                if(dp[i + 1][i + L - 1] > dp[i][i + L - 2])
                {
                    dp[i][i + L - 1] = dp[i + 1][i + L - 1];
                    last[i][i + L - 1] = make_pair(i + 1, i + L - 1);
                }
                else
                {
                    dp[i][i + L - 1] = dp[i][i + L - 2];
                    last[i][i + L - 1] = make_pair(i, i + L - 2);
                }
            }
        }
    int L = 0, R = n - 1;
    bool even = false;
    while(L < R)
    {
        if(s[L] == s[R])
        {
            v.push_back(s[L]);
            if(L + 1 == R)
            {
                even = true;
                break;
            }
        }
        int t = last[L][R].first;
        R = last[L][R].second;
        L = t;
    }
    for(unsigned int i = 0; i < min(v.size(), 50u); i++)
        printf("%c", v[i]);
    if(!even && v.size() < 50) printf("%c", s[L]);//判断中间是否有一个字符出现一次
    for(int i = min((int)v.size() - 1, 49); i >= 0; i--)
        printf("%c", v[i]);
    return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
区间DP是一种动态规划的方法,用于解决区间范围内的问题。在Codeforces竞赛中,区间DP经常被用于解决一些复杂的字符串或序列相关的问题。 在区间DP中,dp[i][j]表示第一个序列前i个元素和第二个序列前j个元素的最优解。具体的转移方程会根据具体的问题而变化,但是通常会涉及到比较两个序列的元素是否相等,然后根据不同的情况进行状态转移。 对于区间长度为1的情况,可以先进行初始化,然后再通过枚举区间长度和区间左端点,计算出dp[i][j]的值。 以下是一个示例代码,展示了如何使用区间DP来解决一个字符串匹配的问题: #include <cstdio> #include <cstring> #include <string> #include <iostream> #include <algorithm> using namespace std; const int maxn=510; const int inf=0x3f3f3f3f; int n,dp[maxn][maxn]; char s[maxn]; int main() { scanf("%d", &n); scanf("%s", s + 1); for(int i = 1; i <= n; i++) dp[i][i] = 1; for(int i = 1; i <= n; i++) { if(s[i] == s[i - 1]) dp[i][i - 1] = 1; else dp[i][i - 1] = 2; } for(int len = 3; len <= n; len++) { int r; for(int l = 1; l + len - 1 <= n; l++) { r = l + len - 1; dp[l][r] = inf; if(s[l] == s[r]) dp[l][r] = min(dp[l + 1][r], dp[l][r - 1]); else { for(int k = l; k <= r; k++) { dp[l][r] = min(dp[l][r], dp[l][k] + dp[k + 1][r]); } } } } printf("%d\n", dp[n]); return 0; } 希望这个例子能帮助你理解区间DP的基本思想和应用方法。如果你还有其他问题,请随时提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值