求最长不重复子串

1.穷举法,每往后查找一个字符,将其与前面的串比较一遍,不重复将其加到前面的串里,否则从前面串的下一个字符再次进行同样的循环。(这里当该字符与前面串第一个重复时才不浪费时间,否者会多进行很多次无用的比较)

void GEtDIfNUm1(char* str)
{
    int maxlen=0;
    int begin=0;
    int len=strlen(str);
    int flag;
    if(len==1)   //防止一个字符那种情况
    {
        cout<<str[0]<<endl;
        return;
    }
    for(int i=0;i<len-1;i++)
    {
        flag=0;
        begin=0;
        for(int j=i+1;j<len;j++)
        {
            for(int m=i;m<j;m++)
            {
                if(str[m]==str[j])
                {
                    if(j-i>maxlen)
                    {
                        maxlen=j-i;
                        begin=i;

                    }
                    flag=1;
                    break;
                }
            }
            if(flag==1)
               break;
        }
    }
    for(int i=begin;i<maxlen;i++)
      cout<<str[i];
    cout<<maxlen<<endl;
}
时间复杂度:O(n^3)

2.hash表去记录重复的字符

int GEtDIfNUm2(char* str)
{
    int hash[250];
    int len=strlen(str);
    int begin=0;
    int maxNUm=0;
    for(int i=0;i<len-1;i++)
    {
        memset(hash,0,sizeof(hash));
        hash[str[i]]=1;
        for(int j=i+1;j<len;j++)
        {
            if(hash[str[j]]==1)
            {
                begin=i;
                if(j-i>maxNUm)
                {
                   maxNUm=j-i;
                   break;
                }
            }
        }
    }
    return maxNUm;
}
时间复杂度:O(n^2)
3.hash表加dp

一.构造first数组和next数组,first数组是指包括当前字符的后面第一次出现重复字符的下标,next数组指和当前字符重复的下一个位置的下标,如果出现同一个字符的多次重复的情况,则每次取它后面距离最近的那个字符的下标。最后就是求所有情况中first[i]-i最大的那个。

int GEtDIfNUm3(char* str)
{
    int MAxLEn=0;
    int BEgin=0;
    int n=strlen(str);
    int* first=(int*)malloc(sizeof(int)*(n+1));
    int* next=(int*)malloc(sizeof(int)*n);
    int hash[256];
    first[n]=n;
    memset(hash,n,sizeof(hash));
    for(int i=n-1;i>=0;i--)
    {
        next[i]=hash[str[i]];
        hash[str[i]]=i;
        if(next[i]<first[i+1])
        {
            first[i]=next[i];
        }
        else
        {
            first[i]=first[i+1];
        }
    }
    for(int i=0;i<n;i++)
    {
        if(first[i]-i>MAxLEn)
        {
            MAxLEn=first[i]-i;
            BEgin=i;
        }
    }
    for(int i=BEgin;i<BEgin+MAxLEn;i++)
    {
        cout<<str[i];
    }
    cout<<endl;
    return MAxLEn;


}
时间复杂度:O(n)

二.在上面的一点改进,上面更加像从后往前,这里就从前往后了,visit数组保留的是当前字符的下标,后面出现重复的时就会进行比较,如果该重复的字符在目前比较的字符串中间的话,那么就从下一个字符串开始比较,下一个字符串开始的位置就是该重复字符的下一个字符,如果不在中间,就把该字符加到目前字符串中,并把他的下标改为他目前的位置。

int GEtDIfNUm4(char* str)
{
   int visit[256];
   memset(visit,-1,sizeof(visit));
   int n=strlen(str);
   int CUrLEn=1;
   int BEgin;
   int MAxLEn=0;
   int lastBEgin=0;

   visit[str[0]]=0;
   for(int i=1;i<n;i++)
   {
       if(visit[str[i]]==-1)
       {
           CUrLEn++;
           visit[str[i]]=i;
       }
       else
       {
           if(lastBEgin<visit[str[i]])    //这里那些没必要进行的比较删除了,上面穷举这里要从头开始,而这里只是从那个重复的字符后面开始
           {
               CUrLEn=i-visit[str[i]];
               lastBEgin=visit[str[i]]+1;
           }
           else
           {
               CUrLEn++;
           }
           visit[str[i]]=i;
       }
       if(CUrLEn>MAxLEn)
       {
           MAxLEn=CUrLEn;
           BEgin=lastBEgin;
       }
   }
   for(int i=BEgin;i<BEgin+MAxLEn;i++)
   {
       cout<<str[i];
   }
   cout<<endl;
   return MAxLEn;

}
时间复杂度:O(n)

4.使用后缀数组,个人觉得这种最好理解(首先要理解后缀数组是啥,前缀数组是啥)
计算每个后缀数组的不重复的前缀数组的长度,然后比较求最长的那个长度就可以了。

int GEtLOngestDiLEn(char* str)
{
   int hash[256];
   int LEn=0;
   memset(hash,0,sizeof(hash));
   while(*str&&!hash[*str])
   {
       LEn++;
       hash[*str]=1;
       str++;
   }
   return LEn;
}
int GEtDIfNUm5(char* str)
{
   int MAxLEn=0;
   int CUrLEn;
   int n=strlen(str);
   int BEgin;
   for(int i=0;i<n;i++)
   {
       CUrLEn=GEtLOngestDiLEn(&str[i]);
       if(CUrLEn>MAxLEn)
       {
           MAxLEn=CUrLEn;
           BEgin=i;
       }
   }
   for(int i=BEgin;i<BEgin+MAxLEn;i++)
   {
       cout<<str[i];
   }
   cout<<endl;
   return MAxLEn;
}
时间复杂度:O(n^2)

主要借鉴了下面的文章

/*
http://blog.csdn.net/insistgogo/article/details/7830972
http://www.cnblogs.com/luxiaoxun/archive/2012/10/02/2710471.html
*/






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值