回文子串&最长公共子串

一个字符串的子串是字符串中连续的一个序列,而一个字符串的子序列是字符串中保持相对位置的字符序列,譬如,"adi"可以使字符串"abcdefghi"的子序列但不是子串。这也就决定了在解这两种"LCS"问题上的一些区别。
Longest-Common-Substring和Longest-Common-Subsequence是不一样的。

之前我写的时候这两个概念有错误,见谅。

把子序列改成字串后条件更严苛了,某些情况下解题的复杂度也低一些。

回文子串

比如drabfbaz里面abfba就是回文的,长度为5.

思路1:逐个字母判断

我们以每个字母为中心,逐个判断字符两边的回文长度。

由于比较简单,就不多说了:

//solution1: make i as center and extends 
int solution1(char* s) 
{
    int n=strlen(s);
    int i=0,j=0;
    int curmax=0,max=0;
    for(i=0;i<n;i++)
    {
        for(j=0;i-j>=0&&i+j<n;j++)//j is the length of substr
        {
            //odd length
            if(s[i+j]!=s[i-j])
            break;
            curmax=j*2+1;
        }
            if(curmax>max)
            max=curmax;
        for(j=0;i-j>=0&&i+j+1<n;j++)//j is the length of substr
            {
                //even length:
                if(s[i-j]!=s[i+j+1])
                break;
                curmax=j*2+2;
            }
                if(curmax>max)
                max=curmax;
        
    }
    return max;
}

解法2:manacher算法

参考:https://www.felix021.com/blog/read.php?2040

我们上面的解法分了奇数长度和偶数长度两种情况讨论,并且复杂度也不低,我们尝试着改进一下:

  • 首先,我们解决奇数和偶数:我们在字符串里面插入#号后看看发生了什么:
    abbc->#a#b#b#a# abdba->#a#b#d#b#a#
    这样所有的都变成了奇数长度,就可以统一处理了。

  • 我们还可以在字符串首加入另一个符号比如$来将字符串的下标0转换为更熟悉的下标1。

以字符串12212321为例,经过上一步,变成了 S[] = "$#1#2#2#1#2#3#2#1#";

  • 然后用一个数组 P[i] 来记录以字符S[i]为中心的最长回文子串向左/右扩张的长度(包括S[i],也就是把该回文串“对折”以后的长度),比如S和P的对应关系:
    S # 1 # 2 # 2 # 1 # 2 # 3 # 2 # 1 #
    P 1 2 1 2 5 2 1 4 1 2 1 6 1 2 1 2 1
    (p.s. 可以看出,P[i]-1正好是原字符串中回文串的总长度)

  • 所以我们的问题转化为,如何计算p[i]的值

  • 我们来发现一条规律,来从一定程度上简化计算:
    假如我们位置为id的地方,其长度为p[id],记右边界为mx=id+p[id];
    (实际上是mx-1吧)
    那么其左边界就是id-p[id]

  • 假设有一个位置i,其正好在id这个字符为中心的回文字符串里面(i<mx),那么根据回文的对称性,其左边会有一个与之对称的位置j,j=2*id-i

位置关系
  • 那么这个能干什么呢?因为我们是从左往右边求的,所以直接可以得到右边位置i的最小回文长度!
    因为回文的对称性,左边的长度如果是x的话,且j-x>mx的对称点,即位置j的回文子串全部在以id为中心的字符串里面的话,可以推出以i为中心的回文字符串的长度最小也是j的回文长度p[j];

  • 但是如果j的回文字符串超出了mx对称点的边界,也可以说,p[i]的值最少为mx-i

  • 得到上述两种情况的最小长度后,我们再对位置i的字符进行判断,是否有更长的字符满足回文。

这些思路转化为代码就是:

for(i = 1; s[i] != '\0'; i++)
 { 
 p[i] = mx > i ? min(p[2*id-i], mx-i) : 1; 
while(s[i + p[i]] == s[i - p[i]]) 
p[i]++; 
if(i + p[i] > mx) 
{    
mx = i + p[i];    
id = i;  }
}

把之前的内容整理下:

  • 预处理字符串的函数:
char c[1000];
int func(char* s)
{
    if (!s)
        return NULL;
    int n = strlen(s);
    
    c[0] = '$';
    int i = 0, j = 1;
    while (i < n)
    {
        c[j++] = '#';
        c[j++] = s[i++];
    }
    c[j] = '#';
    
    return j;
}

主干:

int solution_manacher(char* s)
{
    if (!s)
        return 0;
    int len = func(s);//length of c
    int i = 1;
    int curmax = 0;
    int p[1000];//p[i] is the length of ith char in c
    p[0] = 0;
    int id=1 , mx = 0;//mx=id+p[id],but id=????????
    for (i = 1; i<len; i++)
    {
        p[i] = 1;
        //if (mx>i)
        //  p[i] = minTwo(mx - i, p[2 * id - i]);//actually pi>=minTwo
        //else p[i] = 1;//one char,itself
        if (mx > i)
        { 
            p[i] = p[2 * id - i];
            if (mx - i < p[i])
                p[i] = mx - i;
        }
        while (c[i + p[i]] == c[i - p[i]])
            p[i]++;
        if (i + p[i]>mx)
        {
            mx = i + p[i];
            id = i;
        }
        if (p[i]>curmax)
            curmax = p[i];
    }
    return curmax-1;
}

最长公共子串

由于这个情况限制只能是连续的,所以情况要简单一些,方程也退化为:

l c substring

思路和上次差不多,也是创建了一个二维数组:

#include <iostream>

int lcsubstr(char* s1, char* s2)
{
    if (!s1 || !s2)
        return 0;
    int len1 = strlen(s1);
    int len2 = strlen(s2);
    int i = 0, j = 0;

    int max=0;
    int a[100][100];
    memset(a, 0, sizeof(a));
    for (i = 0; i < len1; i++)
    {
        for (j = 0; j < len2; j++)
        {
            if (s1[i] == s2[j])
                a[i + 1][j + 1] = 1+a[i][j];//careful! i+1&j+1
            if (a[i + 1][j + 1]>max)
                max = a[i + 1][j + 1];
        }
    }
    return max;
}
int main()
{
    char s1[] = "abcd";
    char s2[] = "bcdef";
    std::cout << lcsubstr(s1, s2);
}

不知道有没有bug额。。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,117评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,328评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,839评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,007评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,384评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,629评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,880评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,593评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,313评论 1 243
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,575评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,066评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,392评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,052评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,082评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,844评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,662评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,575评论 2 270

推荐阅读更多精彩内容

  • 字符串最长回文子串 题目描述: 给定一个字符串,求它的最长回文子串的长度。 分析和解法: 最容易想到的办法是枚举所...
    MinoyJet阅读 586评论 0 2
  • 最长回文子串——Manacher 算法 1. 问题定义 最长回文字符串问题:给定一个字符串,求它的最长回文子串长度...
    果哥爸阅读 2,684评论 0 6
  • 上一篇KMP算法之后好几天都没有更新,今天介绍最长回文子串。 首先介绍一下什么叫回文串,就是正着读和倒着读的字符顺...
    zero_sr阅读 2,159评论 2 8
  • 首先用一个非常巧妙的方式,将所有可能的奇数/偶数长度的回文子串都转换成了奇数长度:在每个字符的两边都插入一个特殊的...
    Chris_C阅读 149评论 0 0
  • 计算机二级C语言上机题库(南开版) 1.m个人的成绩存放在score数组中,请编写函数fun,它的功能是:将低于平...
    MrSunbeam阅读 6,093评论 1 42