| 网站首页 | 业界新闻 | 小组 | 威客 | 人才 | 下载频道 | 博客 | 代码贴 | 在线编程 | 编程论坛
欢迎加入我们,一同切磋技术
用户名:   
 
密 码:  
共有 3269 人关注过本帖, 1 人收藏
标题:QA输出最长不重复子串(附加思维流程图)
只看楼主 加入收藏
吹水佬
Rank: 16Rank: 16Rank: 16Rank: 16
等 级:版主
威 望:451
帖 子:10538
专家分:42927
注 册:2014-5-20
收藏
得分:0 
意犹未尽~
此类字符串搜寻问题,方法可能有好多,至于那种好,有时也说不定。
就如文件压缩之类,要看被压缩文件的性质和特点,如音频文件与图像文件,很难用一种算法去压缩所有类型文件,都能做到高效和高压缩率。

字符串搜寻问题,是字符比对过程。如果要每个字符比对,用什么算法都怕会差不多。视实际情况,如果尽可能地跳过更多的字符不用比对,效率就会相对提高。

当然,不管什么算法,从程序代码看,优化代码也很重要。
2016-12-13 04:27
吹水佬
Rank: 16Rank: 16Rank: 16Rank: 16
等 级:版主
威 望:451
帖 子:10538
专家分:42927
注 册:2014-5-20
收藏
得分:0 
字符串搜寻,个人比较喜欢Sunday算法,类似Sunday算法也有,但Sunday算法给人的感觉就是够清爽简洁,Sunday算法比对过程不用去搜寻子串,需要时可一步定位子串中的字符位置。
25楼的代码也按照Sunday算法的思想,巧妙就在于使用了:
子串数组:char chr[256]={0};
数组元素下标为字符码:chr[(unsigned char)s[j]];
数组元素值为其在子串中的序号:chr[(unsigned char)s[j]] = j-i+1;
这样在需要搜寻子串中某一字符时,就可以一步定位,从而减少很多字符比对过程: i += chr[(unsigned char)s[j]];
2016-12-13 05:09
吹水佬
Rank: 16Rank: 16Rank: 16Rank: 16
等 级:版主
威 望:451
帖 子:10538
专家分:42927
注 册:2014-5-20
收藏
得分:0 
以下是引用linlulu001在2016-12-11 21:55:41的发言:

还是有bug。如果只是针对ababcdab基本上没问题。
如果是随机输入,那么还是存在问题。
例如:ababcdadcbabdcabdf

只是找最长的子串很容易,没必要搞的那么复杂,
以字符串限制为26个小写字母为例,你只要声明一个字符数组a[26],用memset初始化0,出现过的字母标记,
如果碰到重复出现的字母就记录它的长度,比之前的子串长就替换之前的子串。过一遍字符串,最长的子串就出来了。

实际应用中能达到目的就是好,确是没必要搞得那么复杂。
但作为学习探讨问题,能更深入或扩展一点未尝不是好事。
有时能更深入些(纵向)、更扩展些(横向),是会有更好感觉的,你懂的。
2016-12-13 05:23
吹水佬
Rank: 16Rank: 16Rank: 16Rank: 16
等 级:版主
威 望:451
帖 子:10538
专家分:42927
注 册:2014-5-20
收藏
得分:0 
回复 28楼 xzlxzlxzl
年底都忙,日常空闲时间不多,没细看贴。
现在大多是早睡早起,利用早上一点时间做些自己的事。
2016-12-13 05:32
xzlxzlxzl
Rank: 15Rank: 15Rank: 15Rank: 15Rank: 15
来 自:湖北
等 级:贵宾
威 望:125
帖 子:1091
专家分:5825
注 册:2014-5-3
收藏
得分:0 
回复 32楼 吹水佬
跳过无需比较的位置原来叫suanday算法!
看来大家都青睐查表法,查表法优点是一次扫描定位,不需要倒查的那个循环,缺点是要花时间初始化表,如果是一个很短的字符串就有点大炮打蚊子了。查表效率体现在范围小、数据量大上,比如纯粹的英文应该只需要一个128个元素的表,而英汉混合的则需要65536个元素的表,前面几位范例都用了256个元素,个人感觉未做到最优。既然都青睐查表法,我也贴一个查表法的例子,希望能得到大神指点:
程序代码:
#include<stdio.h>
void maxlen(char* s, int *start, int *len)
{
    //查表法查找字符串中最长不重复子串,返回子串起点在start,子串长度在len
    int i,j,l,a[128]={0};
    for(l=0; s[l]; l++); //首先获取原始字符串长度在l里
    if(*start<0||*start>=l) *start=0;  //起始位置未初始化的处理
    for(i=*start,*len=0; i+*len<l;i++)
    {
        for(j=i;s[j]&&(a[s[j]]<=i||a[s[j]]==j+1);j++)
            a[s[j]]=j+1; //记录下相应字符在字符串中的位置以备查表 ,弄清楚这个循环条件就明白了,所谓查表法就在这个循环条件里
        if(j-i>*len)
        {
            *start=i;
            *len=j-i;
        }
        if(a[s[j]]-1>i)i=a[s[j]]-1;  //原来这一句就叫sunday算法,对出现重复字符跳过中间字符,不是逐个比较
        //我在20楼指出的错误就在这里,跳过的位置错了,应跳至相同字符的前一个字符后面,我跳到后一个字符后面了。
    }
}
void main()
{
    //char str[80]="12345611718";
    char str[80]="3212134532156";
    int i,j,s,l,m,e;
    //gets(str);
    for(e=0; str[e]; e++);
    for(i=0,m=0; i+m<=e; i++)
    {
        //输出所有最长子串"abcdab: abcd bcda cdab"
        s=i;
        maxlen(str,&s,&l);
        if(m==0)m=l;
        if(s>i)i=s;
        if(l>=m)
        {
            for(j=0; j<l; j++)printf("%c",str[j+s]); //根据最长子串起点和长度输出该子串所有字符
            printf("\n");
        }
    }
}


[此贴子已经被作者于2016-12-13 11:15编辑过]

2016-12-13 11:06
azzbcc
Rank: 19Rank: 19Rank: 19Rank: 19Rank: 19Rank: 19
来 自:江西财经大学
等 级:贵宾
威 望:81
帖 子:3293
专家分:12919
注 册:2012-11-4
收藏
得分:0 
程序代码:
#include "main.h"

int main(int argc, char *argv[])
{
    char *str = "121234121234";

    int max = 1, sa[12] = {1};

    // init sa
    for (int i = 1; str[i]; ++i)
    {
        int pos = i;
        while (--pos >= i - sa[i - 1] && str[pos] != str[i]);

        sa[i] = i - pos;
        if (max < sa[i]) max = sa[i];
    }

    // output
    for (int i = 0; str[i]; ++i)
    {
        if (sa[i] == max)
        {
            int pos = i - sa[i] + 1;
            printf("%d ", pos);
            while (pos <= i) printf("%c", str[pos++]);
            printf("\n");
        }
    }

    return 0;
}
收到的鲜花
  • xzlxzlxzl2016-12-13 11:40 送鲜花  10朵   附言:很精炼!不过最长串个数超过13个就爆了
  • 九转星河2016-12-13 13:03 送鲜花  10朵   附言:学习了,值得借鉴


[fly]存在即是合理[/fly]
2016-12-13 11:18
吹水佬
Rank: 16Rank: 16Rank: 16Rank: 16
等 级:版主
威 望:451
帖 子:10538
专家分:42927
注 册:2014-5-20
收藏
得分:0 
以下是引用xzlxzlxzl在2016-12-13 11:06:21的发言:

跳过无需比较的位置原来叫suanday算法!
看来大家都青睐查表法,查表法优点是一次扫描定位,不需要倒查的那个循环,缺点是要花时间初始化表,如果是一个很短的字符串就有点大炮打蚊子了。查表效率体现在范围小、数据量大上,比如纯粹的英文应该只需要一个128个元素的表,而英汉混合的则需要65536个元素的表,前面几位范例都用了256个元素,个人感觉未做到最优。

个人理解,suanday算法只是巧妙地用了一个子串数组。
至于这个数组元素一般情况是 1<= n <= 256,也就是ASCII码表的编码范围。但实际应用要视具体情况可缩小。
这个子串数组主要是用来记录子串中不重复的字符顺序,suanday算法对于子串中重复出现的字符,只记录最右边的那个字符序号。所以,初始化时只扫描一次子串就可以。无论子串长短,只遍历一次总比每次需要时去搜寻效率高。

2016-12-13 12:06
九转星河
Rank: 19Rank: 19Rank: 19Rank: 19Rank: 19Rank: 19
来 自:长长久久
等 级:贵宾
威 望:52
帖 子:5023
专家分:14003
注 册:2016-10-22
收藏
得分:0 
大家辛苦了,真是感谢大神们的鼎力相助啊看来我还是低估了这条题的价值量啊,原本我只是当一条难度稍大的常规题来做……不过回帖的收获大大超出我的意料,早知我给个百分帖算了。总之,算法原理大体相似,只是算法的花样较多而已,我对比了一下总多算法,无论儿子怎么不同,大都是出自同一个父亲的~不过,能尽量去优化代码还是很好的~        学习了

[code]/*~个性签名:bug是什么意思?bug是看上去没有可能的东西实际上是有可能做到的 就是这样~2018-08-08更~*/[/code]
2016-12-13 13:00
快速回复:QA输出最长不重复子串(附加思维流程图)
数据加载中...
 
   



关于我们 | 广告合作 | 编程中国 | 清除Cookies | TOP | 手机版

编程中国 版权所有,并保留所有权利。
Powered by Discuz, Processed in 0.017553 second(s), 9 queries.
Copyright©2004-2024, BCCN.NET, All Rights Reserved