最长回文子串——DP

本文介绍了一种使用动态规划方法来寻找给定字符串中最长回文子序列的算法实现。通过填充一个二维DP表格,该算法可以高效地解决这一问题。适用于字符串处理和算法设计的学习者。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

dp[i][j]  从i到j的子序列是否是回文

#include "cxf_acm.h"

typedef long long ll;
#define PI 3.1415926535897932
#define E 2.718281828459045
#define INF 0x3f3f3f3f
#define mod 123456789


const int M=1005;
int n,m;
int cnt;
int sx,sy,sz;
int mp[M][12];
int pa[M*10],rankk[M];
int head[M*6],vis[M*10];
double dis[M][10];
ll prime[M*1000];
bool isprime[M*1000];
int lowcost[M],closet[M];
char st1[5050],st2[5050];
int len[M*6];
typedef pair<int ,int> ac;
vector<int> g[M*10];
int dp[2005][2005];
int has[105][105];
int month[13]= {0,31,59,90,120,151,181,212,243,273,304,334,0};
int dir[8][2]= {{0,1},{0,-1},{-1,0},{1,0},{1,1},{1,-1},{-1,1},{-1,-1}};


void getpri()
{
    ll i;
    int j;
    cnt=0;
    memset(isprime,false,sizeof(isprime));
    for(i=2; i<1000000LL; i++)
    {
        if(!isprime[i])prime[cnt++]=i;
        for(j=0; j<cnt&&prime[j]*i<1000000LL; j++)
        {
            isprime[i*prime[j]]=1;
            if(i%prime[j]==0)break;
        }
    }
}
ll qk_mul(ll a,ll b,ll mo)
{
    ll t=0;
    while(b)
    {
        if(b&1)
            t=(t+a)%mo;
        a=(a<<1)%mo;
        b>>=1;
    }
    t%=mo;
    return t;
}
ll qk_mod(ll a,ll b,ll mo)
{
    ll ans=1;
    while(b)
    {
        if(b&1) ans=qk_mul(ans,a,mo);
        a=qk_mul(a,a,mo);
        b>>=1;
    }
    ans%=mo;
    return ans;
}
int gcd(int a,int b)
{
    return b == 0 ? a : gcd(b, a%b);
}
int lcm(int a,int b)
{
    return a*b/gcd(a,b);
}
int findx(int t)
{
    if(t!=pa[t])
        pa[t]=findx(pa[t]);
    return pa[t];
}
void unionx(int x,int y)
{
    x=findx(x);
    y=findx(y);
    if(x!=y)
    {
        pa[y]=x;
    }
}


queue<nodei *>qu;


void init()
{
    for(int i=0; i<101; i++)
        pa[i]=i;
}
int heap[100005];
void push(int x)
{
    int i=++sz;
    while(i>1)  //i>0
    {
        int p=i/2; //(i-1)/2
        if(heap[p]<=x)break;
        heap[i]=heap[p];
        i=p;
    }
    heap[i]=x;
    /* a[++sz] = x;
     int t = a[sz];
     int tson = sz;
     while( (tson > 1)&&( a[tson/2] > t))
     {
         a[tson] = a[tson/2];
         tson = tson/2;
     }
     a[tson] = x;
    */
}
int pop()
{
    int ret=heap[1];//int ret=a[0]
    int x=heap[sz--];//a[--sz]
    int i=1;// i=0
    while(2*i<sz) //2*i+1
    {
        int a=i*2,b=i*2+1;
        if(b<sz&&heap[b]<heap[a])a=b;
        if(heap[a]>=x)break;
        heap[i]=heap[a];
        i=a;
    }
    heap[i]=x;
    return ret;
}
int main()
{
    int i,j,t,k=0;
    string str;
    getline(cin,str);
    memset(dp,0,sizeof(dp));
    for(i=0; i<str.length(); i++)
        for(j=i; j>=0; j--)
        {
            if(str[i]==str[j]&&((i-j)<2||dp[j+1][i-1]==1))
            {
                dp[j][i]=1;
                k=max(k,i-j+1);
            }
        }
    printf("%d\n",k);
    return 0;
}
### Python 实现最长回文子串算法 #### 方法一:暴力搜索法 暴力搜索法通过遍历所有可能的子串并检查其是否为回文来解决问题。这种方法简单直观,但时间复杂度较高。 ```python def longestPalindrome_brute_force(s): def is_palindrome(substring): return substring == substring[::-1] n = len(s) max_len = 0 start = 0 for i in range(n): for j in range(i, n): if is_palindrome(s[i:j+1]) and (j - i + 1) > max_len: max_len = j - i + 1 start = i return s[start:start + max_len] ``` 此方法的时间复杂度为 O()[^1]。 #### 方法二:动态规划法 动态规划法利用了回文的特点——如果一个字符串是回文,则去掉两端字符后的部分仍然是回文。这使得可以通过构建二维表的方式优化查找过程。 ```python def longestPalindrome_dp(s): n = len(s) dp = [[False] * n for _ in range(n)] start = 0 maxLength = 1 for i in range(n): dp[i][i] = True for cl in range(2, n + 1): for i in range(n - cl + 1): j = i + cl - 1 if s[i] == s[j] and cl == 2: dp[i][j] = True elif s[i] == s[j]: dp[i][j] = dp[i + 1][j - 1] if dp[i][j] and cl > maxLength: start = i maxLength = cl return s[start:start + maxLength] ``` 该方法的时间复杂度为 O()[^2]。 #### 方法三:中心扩展法 中心扩展法则基于这样一个事实:任何长度大于等于3的回文都至少有一个中心位置。可以从每个潜在的中心向两侧扩散直到遇到不匹配为止。 ```python def expand_around_center(s, left, right): L, R = left, right while L >= 0 and R < len(s) and s[L] == s[R]: L -= 1 R += 1 return R - L - 1 def longestPalindrome_expand(s): if not s or len(s) < 1: return "" start = end = 0 for i in range(len(s)): len1 = expand_around_center(s, i, i) len2 = expand_around_center(s, i, i + 1) maxLen = max(len1, len2) if maxLen > end - start: start = i - (maxLen - 1) // 2 end = i + maxLen // 2 return s[start:end + 1] ``` 此方法同样具有较好的性能表现,在实际应用中往往能取得接近线性级别的效率[^4]。 #### 方法四:Manacher算法 Manacher算法是一种专门针对这个问题设计的有效解决方案,能够在O(n)时间内完成计算。它通过对原始字符串预处理以及巧妙地维护辅助数组实现了这一目标。 ```python def preprocess_string(s): if not s: return "^$" result = ["^"] for char in s: result.append("#") result.append(char) result.append("#$") return ''.join(result) def manachers_algorithm(s): T = preprocess_string(s) P = [0] * len(T) C = R = 0 for i in range(1, len(T)-1): mirr = 2*C - i if R > i: P[i] = min(R-i, P[mirr]) while T[i+(P[i]+1)] == T[i-(P[i]+1)]: P[i] += 1 if i + P[i] > R: C = i R = i + P[i] maxLen = 0 centerIndex = 0 for i in range(1, len(P)-1): if P[i] > maxLen: maxLen = P[i] centerIndex = i startIndex = (centerIndex - maxLen) // 2 return s[startIndex:startIndex + maxLen] ``` 上述四种方法各有优劣之处,具体选择取决于应用场景和个人偏好。其中Manacher算法由于其优秀的时空特性而被广泛推荐用于解决此类问题[^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值