标题:QA输出最长不重复子串(附加思维流程图)
只看楼主
九转星河
Rank: 19Rank: 19Rank: 19Rank: 19Rank: 19Rank: 19
来 自:长长久久
等 级:贵宾
威 望:52
帖 子:5023
专家分:14003
注 册:2016-10-22
得分:0 
回复 20楼 xzlxzlxzl
其实我也不知道自己测试的代码可不可靠,我只是在每个循环初加入输出一个字符,然后统计输出字符的数量,但运用库函数耗时用这法是无法测试出来的。至于精确耗间,还是要用time.h头文件来测试,不过对于小型字符串来说,用time.h相差不会太明显,我也没用time.h测试了,初步估计少于1毫秒。这完全可以自己简单测试执行效率~

[code]/*~个性签名:bug是什么意思?bug是看上去没有可能的东西实际上是有可能做到的 就是这样~2018-08-08更~*/[/code]
2016-12-11 19:54
linlulu001
Rank: 13Rank: 13Rank: 13Rank: 13
等 级:贵宾
威 望:20
帖 子:944
专家分:4047
注 册:2016-4-13
得分:0 
回复 17楼 xzlxzlxzl
还是有bug。如果只是针对ababcdab基本上没问题。
如果是随机输入,那么还是存在问题。
例如:ababcdadcbabdcabdf

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

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

2016-12-11 21:55
xzlxzlxzl
Rank: 15Rank: 15Rank: 15Rank: 15Rank: 15
来 自:湖北
等 级:贵宾
威 望:125
帖 子:1091
专家分:5825
注 册:2014-5-3
得分:0 
感谢@linlulu001版主的耐心!该bug已经处理,主要是优化子串起始位置有误。
万事怕认真,针对@九转星河版主的疑问,我做了对比测试,至于结果,就不说了吧!@九转星河的代码主要麻烦事使用了局部静态变量,要想在反复测试中获得正确结果,必须对该静态变量数组做清零操作,我已经尽最大可能的优化该操作,减少清零次数,在循环百万次的情况下,反复测试,还是有相当的差距!

正像@linlulu001版主说的,这么简单的问题就不要再纠缠了,无意义!祝各位学习愉快!
2016-12-11 22:32
九转星河
Rank: 19Rank: 19Rank: 19Rank: 19Rank: 19Rank: 19
来 自:长长久久
等 级:贵宾
威 望:52
帖 子:5023
专家分:14003
注 册:2016-10-22
得分:0 
回复 9楼 吹水佬
多谢指点,这个我慢慢弄是可以弄出来的,做这题题我的收获还是很大的,是时候该去结贴了~

[code]/*~个性签名:bug是什么意思?bug是看上去没有可能的东西实际上是有可能做到的 就是这样~2018-08-08更~*/[/code]
2016-12-12 13:16
吹水佬
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:版主
威 望:432
帖 子:10064
专家分:41463
注 册:2014-5-20
得分:0 
回复 24楼 九转星河
试了一下,没细测。
/*
    取字符串中不重复最长的子串
    如:abcdab 取出 abcd、bcda、cdab

    字符串...3212134532156....出现2重复
    子串.....321..............下次从2后面的1开始

    字符串...3212134532156....出现1重复  
    子串.......12.............下次从1后面的2开始  

    字符串...3212134532156....出现3重复  
    子串........21345.........下次从3后面的4开始   

    字符串...3212134532156....出现5重复  
    子串...........45321......下次从5后面的3开始   

    字符串...3212134532156....结束
    子串.............32156
  
    不重复最长子串  21345、45321、32156
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

unsigned long *_maxlist(char *s, int *count, int *maxlen)
{
    if (!*s)
        return NULL;
    char *p;
    char chr[256]={0};
    int len=strlen(s);
    int i, j, max;
    unsigned long *maxlist=(unsigned long *)malloc(sizeof(unsigned long));
    *maxlen = 0;
    *count = 0;
    i = 0;
    while (i <= len-*maxlen)
    {
        j = i;
        while (j<=len)
        {
            if (s[j] && (chr[(unsigned char)s[j]] == 0))    //不是重复字符
            {
                chr[(unsigned char)s[j]] = j-i+1;   //以序号为标记,出现重复时就从这个字符之后重新开始。
                j++;    //继续下一个字符
            }
            else    //是重复字符
            {
                max = j-i;  //字串长度
                if (max > *maxlen)  //是较长字串
                {
                    *maxlen = max;  //保存较长值  
                    *count = 1; //第一个较长的字串
                    maxlist = (unsigned long *)realloc(maxlist, sizeof(unsigned long)); //重建字串地址列表
                    maxlist[0] = (unsigned long)(s+j-max);  //保存字串地址
                }
                else if (max == *maxlen)    //是等长字串
                {
                    *count += 1;  //增加一个字串  
                    maxlist = (unsigned long *)realloc(maxlist, (*count)*sizeof(unsigned long));
                    maxlist[*count-1] = (unsigned long)(s+j-max);
                }
                if (s[j])
                    i += chr[(unsigned char)s[j]];  //下次搜索开始位置为子串中出现重复字符的后一位位置。
                else
                    i++;
                memset(chr, 0, 256);
                break;
            }
        }
    }
    return maxlist;
}

main()
{
    //char s[]="abcdab";
    char s[]="3212134532156";
    int i, maxlen, count;
    unsigned long *maxlist = _maxlist(s, &count, &maxlen);
    printf("字符串:\n%s\n不重复最长子串:\n", s);
    for (i=0; i<count; i++)
        printf("%.*s\n", maxlen, (unsigned long *)maxlist[i]);
    free(maxlist);
}



[此贴子已经被作者于2016-12-12 20:40编辑过]

2016-12-12 20:39
吹水佬
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:版主
威 望:432
帖 子:10064
专家分:41463
注 册:2014-5-20
得分:0 
回复 23楼 xzlxzlxzl
可能是我的编译器问题,好象不支持 & 参数,改用 * 后结果有时不对,想互测较对一下。
如:3212134532156
结果:
21345
32156
32156
45321
32156

修改后代码:
#include<stdio.h>
void maxlen(char* s, int *start, int *len)
{
    //查找字符串中最长不重复子串,返回子串起点在start,子串长度在len
    int i,j,k,l;
    for(l=0; s[l]; l++); //首先获取原始字符串长度在l里
    if(*start<0||*start>=l) *start=0;  //起始位置未初始化的处理
    for(i=*start,*len=0; i+*len<l;)
    {
        for(j=i+1; s[j]; j++)
        {
            for(k=j-1; k>i&&s[j]!=s[k]; k--); //倒查是否有重复
            if(s[j]==s[k])break;
        }
        if(j-i>*len)
        {
            *start=i;
            *len=j-i;  //记录当前最大子串的起点位置和子串长度
        }
        i=j;  //原始起点位置不逐步自加,而是等于下一个重复字符起点位置可提高效率,同理循环条件是i+*len<l也是提高执行效率
    }
}

void main()
{
    //char str[80]="bcdaab";
    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;
            i=s;
        }
        if(l>=m)
        {
            for(j=0; j<l; j++)printf("%c",str[j+s]); //根据最长子串起点和长度输出该子串所有字符
            printf("\n");
        }
    }
}
2016-12-12 21:01
吹水佬
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:版主
威 望:432
帖 子:10064
专家分:41463
注 册:2014-5-20
得分:0 
以下是引用xzlxzlxzl在2016-12-11 18:40:14的发言:

感谢指正!
代码未能正确修正最长字符起始比较位置,导致最后一个长子串多输出几次了,修正如下:
    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");
        }
    }

原来还有更新,改为:
    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");
        }
    }

继续测:3212134532156
结果:
21345
32156
漏了45321 ?我是否修改错了?

2016-12-12 21:15
xzlxzlxzl
Rank: 15Rank: 15Rank: 15Rank: 15Rank: 15
来 自:湖北
等 级:贵宾
威 望:125
帖 子:1091
专家分:5825
注 册:2014-5-3
得分:0 
感谢还有耐心啊!看我20楼的,不是说还发现了个bug么,后来没提供修改了。形参使用“&”可能是C++的编译器支持的,反正我用vc、vs都能编译通过,你修改为指针运行正确,现将修改两次bug的代码提供如下(不知道还有没有):
程序代码:
#include<stdio.h>
void maxlen(char* s, int *start, int *len)
{
    //查找字符串中最长不重复子串,返回子串起点在start,子串长度在len
    int i,j,k,l;
    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+1; s[j]; j++)
        {
            for(k=j-1; k>i&&s[j]!=s[k]; k--); //倒查是否有重复
            if(s[j]==s[k])break;
        }
        if(j-i>*len)
        {
            *start=i;
            *len=j-i;  //记录当前最大子串的起点位置和子串长度
        }
        if(k>i)i=k;  
    }
}

void main()
{
    //char str[80]="bcdaab";
    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-12 21:38
九转星河
Rank: 19Rank: 19Rank: 19Rank: 19Rank: 19Rank: 19
来 自:长长久久
等 级:贵宾
威 望:52
帖 子:5023
专家分:14003
注 册:2016-10-22
得分:0 
虽然已经结贴,但我还是感到意犹未尽~对于输出所有最长的不重复子串,经过一段时间酝酿算法后,我发现了还是在原贴的基础上实现的~拿出来show一下~~~~

由于最长子串的长度为定值,所以……realloc就是管用!!!

程序代码:
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define Input_Max 10

char *fun(char *p1,char **p2)
{
    static int ASS[256]={0};

    for (;++ASS[**p2]!=2&&**p2;++*p2);

    if (!**p2)
        return p1;

    for (--ASS[*p1];*p1!=**p2;--ASS[*p1])
          ++p1;

    return p1;
}

int main()
{
    char *s=(char *)malloc(Input_Max);
    char *p1,*p2,*p3;
    char *p_Max=NULL;

    int L,L_Max;
    int n=1;

    L=L_Max=0;

    p1=p2=p3=s;

    for (;(*p1=getchar())!='\n';p1++)
        if (p1-s>=n*Input_Max-1)
            realloc(s,++n*Input_Max);


    *p1='\0';
    p1=s;

    while ((int)strlen(p1)>=L_Max)
    {
        p3=p1;    

        p1=fun(p1,&p2);

        L=p2-p3;

        if (L>L_Max)
        {
            n=1;
            free(p_Max);
            p_Max=(char *)malloc(sizeof(char));
        }

        if (L>=L_Max)
        {
            L_Max=L;

            realloc(p_Max,n*L_Max*sizeof(char));
            memcpy(p_Max+(n-1)*L_Max,p3,L_Max*sizeof(char));

            ++n;
        }

        ++p1;
        ++p2;
    }

    for (p1=p_Max;n-->1&&s[0];p_Max+=L_Max)
        printf("%.*s\n",L_Max,p_Max);

    free(p1);
    free(s);

    return 0;
}

[code]/*~个性签名:bug是什么意思?bug是看上去没有可能的东西实际上是有可能做到的 就是这样~2018-08-08更~*/[/code]
2016-12-13 03:19
九转星河
Rank: 19Rank: 19Rank: 19Rank: 19Rank: 19Rank: 19
来 自:长长久久
等 级:贵宾
威 望:52
帖 子:5023
专家分:14003
注 册:2016-10-22
得分:0 
参考25楼的,又改进了一下算法,不用memcpy,直接用动态指针数组标记所有最长子串的起点~//突然发现自己越来越追求程序的执行效率

程序代码:
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define Input_Max 10

char *fun(char *p1,char **p2)
{
    static int ASS[256]={0};

    for (;++ASS[**p2]!=2&&**p2;++*p2);

    if (!**p2)
        return p1;

    for (--ASS[*p1];*p1!=**p2;--ASS[*p1])
          ++p1;

    return p1;
}

int main()
{
    char *s=(char *)malloc(Input_Max);
    char *p1,*p2,*p3;
    char **p_Max=NULL;

    int L,L_Max;
    int n=1,count=0;

    L=L_Max=0;

    p1=p2=p3=s;

    for (;(*p1=getchar())!='\n';p1++)
        if (p1-s>=n*Input_Max-1)
            realloc(s,++n*Input_Max);


    *p1='\0';
    p1=s;

    while ((int)strlen(p1)>=L_Max)
    {
        p3=p1;    

        p1=fun(p1,&p2);

        L=p2-p3;

        if (L>L_Max)
        {
            n=1;
            free(p_Max);
            p_Max=(char **)malloc(sizeof(char*));
        }

        if (L>=L_Max)
        {
            L_Max=L;

            realloc(p_Max,n*sizeof(char*));
            p_Max[n-1]=p3;

            ++n;
        }

        ++p1;
        ++p2;
    }

    for (;count<(n-1)&&s[0];count++)
        printf("%.*s\n",L_Max,p_Max[count]);

    free(p_Max);
    free(s);

    return 0;
}


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


[code]/*~个性签名:bug是什么意思?bug是看上去没有可能的东西实际上是有可能做到的 就是这样~2018-08-08更~*/[/code]
2016-12-13 04:02



参与讨论请移步原网站贴子:https://bbs.bccn.net/thread-472169-1-1.html




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

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