算法-字符串之最长无重复子串

算法字符串系列的第四篇文章,计算给定字符串的最长无重复子串。


这篇文章主要介绍两种方法,一种是基于hash的思想,一种是基于dp(动态规划)+hash来实现,这两种方法都不难,容易理解。下面会详细介绍。

1.hash实现

这类问题应该学会分析结果的特点,比如说前面的最长回文子串,是利用了回文串的对称性,那么无重复子串呢,想到无重复,可以想到可以使用hash,当新的字符来了,有冲突,就说明前面是有重复的。
算法思想:两次循环得到所有的子串,用hash判断是否重复。

代码
代码中需要注意的地方:
1.以字符对应的ASCII码作为hash值,visit[str[i]]=0,说明str[i]这个字符还没有出现过,=1说明有重复。
2.在每次内层循环重新开始的时候,都要将visit初始化为0,每次内层循环求的是str[i...j]之间的最长子串,要判断他们之间是否有重复,所以要确保i...j这个范围内的visit都没初始化为0了,否则出现i...j之间的字符和这个范围之外的字符重复就会导致结果出错。

/*求所有的子串,用hash判断是否是重复的,*/
void LNRS_hash(char *str, int size)
{
    int i, j;
     for (i = 0; i < size; ++i)
    {
        memset(visit, 0, sizeof(visit));//初始化visit为0
        visit[str[i]] = 1;
        for (j = i + 1; j < size; ++j)
        {
            if (visit[str[j]] == 0)
            {
                visit[str[j]] = 1;
            }
            else
            {
                if (j - i > longest)
                {
                    longest = j - i;
                    start = i;
                }
                break;
             }
        }
        if ((j == size) && (j - i > longest))
        {
            longest = j - i;
            start = i;
        }
    }
}

结果:

基于hash求最长无重复子串

2.动态规划+hash

首先理解,动态规划算法的思想,将问题分解为子问题的解,找到重叠子问题和最优子结构,对需要重复计算的结果进行存储。在这里最优子结构很好理解,在所有无重复子串中,长度最长的就是最优的。而重叠子问题呢?简单的理解,就是当前的LNRS串可以由上一次计算的LNRS得到。

重叠子问题:我们考虑两种情况。

  1. 当前的字符和前面的字符没有重复,那么到当前字符的最长无重复子串就应该在上次求得的LNRS串长度+1,;
  2. 如果当前的字符有冲突,那么有两种情况需要分析。第一,如果和它冲突的字符在当前最长无重复子串的起始位置前面,那么很明显,对计算当前的LNRS串没有影响,还是在上次基础上+1;如果冲突字符在当前的LNRS串开始位置之后,那么就从后一个位置计算无重复子串的长度。

而hash在这里就是帮助我们判断是否重复,而不用回溯子串。

代码
理解了上面提到的重叠子问题之后,下面的问题就简单多了。
代码中需要注意的地方:

  1. dp[]记录第i位的无重复子串,这个子串不一定是全局最长子串,但是针对第i个字符是最长的。所以需要比较所有的dp[i],来确定最长的。
  2. visit[i]在这里不止是当做hash来使用了,它不止用来判断是否重复,还记录最近的重复位置。eg:abcdahijka,针对最后一个a,visit[a]记录的应该是第二个a的位置,记录第一个a的位置毫无意义,因为从第一个a后面的b...到最后一个a之间,还有一个a,所以其实b到第二个a之间的字符肯定不会在a3的最长重复子串内。
/* LNRS dp + hash 记录下标 */
void LNRS_dp_hash(char * str, int size)
{
    int *dp = (int*)malloc(sizeof(int)*size);

    memset(visit, -1, sizeof(visit));
    memset(dp, 0, sizeof(dp));
    dp[0] = 1;
    visit[str[0]] = 0;
    int last_start = 0;

    for (int i = 1; i < size; i++) {
        if (visit[str[i]] == -1){
            dp[i] = dp[i - 1] + 1;
            visit[str[i]] = i;//记录字符的下标
        }
        else {/*和visit[str[i]]位置的字符冲突*/
            if (last_start <= visit[str[i]])
            {
                dp[i] = i - visit[str[i]];
                last_start = visit[str[i]] + 1;
                visit[str[i]] = i;//更新最近的重复位置
          }
            else{
                dp[i] = dp[i - 1] + 1;
                visit[str[i]] = i;//记录字符的下标
            }
        }

        if (dp[i]>longest)
        {
            longest = dp[i];
            start = i - longest + 1;
        }
    }
}

结果:

dp+hash最长无重复子串

3.dp+hash优化的问题

我们看dp[i] = dp[i-1]+1;可以理解为就是在更新当前i位的最优解,在看代码中更新dp[i]的部分,再使用到dp[i-1]的地方都只是在上一次的基础上+1,看到这里我们就很容易做出优化策略了。可以不用n位长的数组dp[i]来存储当前的最优解,而只用一个变量来记录就可以了,优化方案很简单,就不贴代码了。
优化:用一个变量来代替dp[]。

总结:

最长回文子串的问题:关注子串中的对称关系。
最长无重复子串为题:关注子串中字符的唯一性,善用hash。
动态规划法,对需要重复计算的问题,可以选保存下来,减少计算的时间复杂度。

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

推荐阅读更多精彩内容

  • 背景 一年多以前我在知乎上答了有关LeetCode的问题, 分享了一些自己做题目的经验。 张土汪:刷leetcod...
    土汪阅读 12,719评论 0 33
  • 1. 问题定义 最长不重复子串:一个字符串中最长的没有重复字符的子串。举个🌰 : abcabcbb 最长子串 a...
    林大鹏阅读 11,836评论 0 2
  • 题目 对于一个字符串,请设计一个高效算法,找到字符串的最长无重复字符的子串长度。给定一个字符串A及它的长度n,请返...
    IT_Matters阅读 2,063评论 0 1
  • 上一篇KMP算法之后好几天都没有更新,今天介绍最长回文子串。 首先介绍一下什么叫回文串,就是正着读和倒着读的字符顺...
    zero_sr阅读 2,257评论 2 8
  • 动态规划(Dynamic Programming) 本文包括: 动态规划定义 状态转移方程 动态规划算法步骤 最长...
    廖少少阅读 3,252评论 0 18