日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

最长重复子串和最长不重复子串求解

發(fā)布時間:2023/12/18 编程问答 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 最长重复子串和最长不重复子串求解 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

最長重復(fù)子串和最長不重復(fù)子串求解

本文內(nèi)容框架:

§1 最長重復(fù)子串

基本方法、KMP算法求解、后綴數(shù)組求解

§2 最長不重復(fù)子串

基本方法、動態(tài)規(guī)劃、動態(tài)規(guī)劃+Hash

§3 小結(jié)

?

§1最長重復(fù)子串

?

1.1問題描述

?

首先這是一個單字符串問題。子字符串R 在字符串L 中至少出現(xiàn)兩次,則稱R 是L 的重復(fù)子串。重復(fù)子串又分為可重疊重復(fù)子串和不可重疊重復(fù)子串。

?

1.2基本方法

?

枚舉子串,讓子串和子串進行比較。直接看代碼:

C代碼 ?
  • /*?最長重復(fù)子串?Longest?Repeat?Substring?*/??
  • ???
  • int?maxlen;????/*?記錄最長重復(fù)子串長度?*/??
  • int?maxindex;??/*?記錄最長重復(fù)子串的起始位置?*/??
  • void?outputLRS(char?*?arr);??/*?輸出LRS?*/??
  • ???
  • /*?最長重復(fù)子串?基本算法?*/??
  • int?comlen(char?*?p,?char?*?q)??
  • {??
  • ????int?len?=?0;??
  • ????while(*p?&&?*q?&&?*p++?==?*q++)??
  • ????{??
  • ????????++len;??
  • ????}??
  • ????return?len;??
  • }??
  • ???
  • void?LRS_base(char?*?arr,?int?size)??
  • {??
  • ????for(int?i?=?0;?i?<?size;?++i)??
  • ????{??
  • ????????for(int?j?=?i+1;?j?<?size;?++j)??
  • ????????{??
  • ????????????int?len?=?comlen(&arr[i],&arr[j]);??
  • ????????????if(len?>?maxlen)??
  • ????????????{??
  • ????????????????maxlen?=?len;??
  • ????????????????maxindex?=?i;??
  • ????????????}??
  • ????????}??
  • ????}??
  • ????outputLRS(arr);??
  • }??
  • ?╝①

    優(yōu)化思路

    一般的優(yōu)化方法就是在充分利用已有的結(jié)果,最長重復(fù)子串的長度增加一定是建立在之前已經(jīng)找到的重復(fù)子串之上的,充分利用已找到的重復(fù)子串的位置和長度是優(yōu)化的一個重點,此外還有就是未不是重復(fù)子串的,以后就不會被包含在重復(fù)子串內(nèi),如"ab"只有一個,則重復(fù)子串就不能包含"ab"(允許重疊的重復(fù)子串例外)。

    ?

    1.2KMP算法求解

    ?

    對KMP算法還不是很了解的,可以查看我的另一篇博文(不懂猛點),在KMP算法的關(guān)鍵就是求解next數(shù)組,針對next[j]=k,可以得到P[0,1,...,k-1]=P[j-k,j-k+1,...,j-1]??吹絇[0,1,...,k-1]=P[j-k,j-k+1,...,j-1]應(yīng)該會眼前一亮,大腦頓時清醒些,這不就是重復(fù)子串嗎!由此求解最長重復(fù)子串就轉(zhuǎn)化為求解KMP算法next數(shù)組中的最大值(即max{next[j]=k}。

    現(xiàn)在就是求解next數(shù)組的問題了,還是直接查看代碼:

    ?

    C代碼 ?
  • int?getNext(char?*str,int?*next)??
  • {??
  • ????int?len=strlen(str);??
  • ????int?index=0;??
  • ????int?k=-1;??
  • ????next[0]=k;??
  • ????int?max=0;??
  • ??
  • ????//kmp算法求next值,取得最大的字串??
  • ????while?(index<len)??
  • ????{??
  • ????????if?(k==-1?||?str[index]==str[k])??
  • ????????{??
  • ????????????k++;??
  • ????????????index++;??
  • ????????????next[index]=k;??
  • ????????????if?(k>max)//求得其中重復(fù)最大的字串的個數(shù),也就是與最前面串的重復(fù)數(shù)??
  • ????????????{??
  • ????????????????max=k;??
  • ????????????}??
  • ????????}??
  • ????????else??
  • ????????????k=next[k];??
  • ????}??
  • ??
  • ????return?max;??
  • }??
  • ??
  • int?main()??
  • {??
  • ????char?str[50];//輸入字符串??
  • ????cin>>str;??
  • ????int?max=0;//最大的字串??
  • ????int?nextMax;//接受getNext函數(shù)中返回的最大值??
  • ????int?index;??
  • ????int?maxIndex;//保存最大的字串起始位置??
  • ????int?len=strlen(str);??
  • ????//將一個字符串從開始一直減少到只剩一個字符,通過這個取得最小字串??
  • ????for?(index=0;index<len-1;index++)??
  • ????{??
  • ????????int?*next=new?int[len-index];//取得next在這沒用??
  • ????????nextMax=getNext(str+index,next);//每次從str+index開始??
  • ????????if?(nextMax>max)??
  • ????????{??
  • ????????????max=nextMax;??
  • ????????????maxIndex=index;??
  • ????????}??
  • ????}??
  • ??????
  • ????//輸出最長字串??
  • ????cout<<"最長字串:?";??
  • ????for?(index=0;index<max;index++)??
  • ????{??
  • ????????cout<<str[index+maxIndex];??
  • ????}??
  • ????cout<<endl;??
  • ??????
  • ????return?0;??
  • }??
  • ╝②?

    ?

    1.3后綴數(shù)組求解

    ?

    后綴數(shù)組在我的另外一篇博文有介紹,還沒有概念的可以移步查看點擊。后綴數(shù)組就是保留字符串所有位置到字符串末尾的子字符串,a[i]就是第i位置到末尾的子串。有了后綴數(shù)組,對后綴數(shù)組進行排序,然后進行求后綴數(shù)組相鄰元素的最大前綴就是最大重復(fù)子串。

    ?

    Cpp代碼 ?
  • #include?<iostream>??
  • ?using?namespace?std;??
  • ????
  • ?const?int?MAXN?=?1000;??
  • ??
  • ?int?mycmp(const?void*?p1,?const?void*?p2)??
  • ?{??
  • ?????return?strcmp(*(char*?const*)p1,?*(char*?const*)p2);??
  • ?}??
  • ???
  • ?int?getLen(char*?p,?char*?q)??
  • ?{??
  • ?????int?ret?=?0;??
  • ?????while?(*p?&&?*p++?==?*q++)??
  • ?????{??
  • ?????????++ret;??
  • ?????}??
  • ?????return?ret;??
  • ?}??
  • ???
  • ?char*?foo(char?result[],?char?s[])??
  • ?{??
  • ?????int?len?=?strlen(s);??
  • ?????char**?suffix?=?new?char*[len];??
  • ?????for?(int?i?=?0;?i?<?len;?++i)??
  • ?????{??
  • ?????????suffix[i]?=?s?+?i;??
  • ?????}??
  • ?????qsort(suffix,?len,?sizeof?(char*),?mycmp);??
  • ?????//for?(int?i?=?0;?i?<?len;?++i)??
  • ?????//{??
  • ?????//????cout?<<?suffix[i]?<<?endl;??
  • ?????//}??
  • ?????int?maxlen?=?0,?maxi?=?0,?maxj?=?0,?temp?=?0;??
  • ?????for?(int?i?=?0;?i?<?len?-?1;?++i)??
  • ?????{??
  • ?????????temp?=?getLen(suffix[i],?suffix[i?+?1]);??
  • ?????????if?(temp?>?maxlen)??
  • ?????????{??
  • ?????????????maxlen?=?temp;??
  • ?????????????maxi?=?i;??
  • ?????????????maxj?=?i?+?1;??
  • ?????????}??
  • ?????}??
  • ?????//cout?<<?maxlen?<<?endl;??
  • ?????//cout?<<?suffix[maxi]?<<?endl;??
  • ?????//cout?<<?suffix[maxj]?<<?endl;??
  • ?????//printf("%.*s\n",?maxlen,?suffix[maxi]);??
  • ?????for?(int?i?=?0;?i?<?maxlen;?++i)??
  • ?????{??
  • ?????????result[i]?=?suffix[maxi][i];??
  • ?????}??
  • ?????result[maxlen]?=?'\0';??
  • ?????return?result;??
  • ?}??
  • ???
  • ?int?main()??
  • ?{??
  • ?????char?s[MAXN];??
  • ?????char?result[MAXN];??
  • ?????while?(cin?>>?s)??
  • ?????{??
  • ?????????cout?<<?foo(result,?s)?<<?endl;??
  • ?????}??
  • ?????return?0;??
  • }??
  • ?

    ╝③

    §2最長不重復(fù)子串?

    2.1問題描述

    ?

    從一個字符串中找到一個連續(xù)子串,該子串中任何兩個字符不能相同,求子串的最大長度并輸出一條最長不重復(fù)子串。

    下面介紹四種方法逐步優(yōu)化,時間復(fù)雜度從O(n2)到O(n)。

    ?

    2.2基本算法(使用hash)

    ?

    要求子串中的字符不能重復(fù),判重問題首先想到的就是hash,尋找滿足要求的子串,最直接的方法就是遍歷每個字符起始的子串,輔助hash,尋求最長的不重復(fù)子串,由于要遍歷每個子串故復(fù)雜度為O(n^2),n為字符串的長度,輔助的空間為常數(shù)hash[256]。

    ?

    C代碼 ?
  • /*?最長不重復(fù)子串?設(shè)串不超過30?
  • ?*?我們記為?LNRS?
  • ?*/??
  • int?maxlen;??
  • int?maxindex;??
  • void?output(char?*?arr);??
  • ???
  • /*?LNRS?基本算法?hash?*/??
  • char?visit[256];??
  • ???
  • void?LNRS_hash(char?*?arr,?int?size)??
  • {??
  • ????int?i,?j;??
  • ????for(i?=?0;?i?<?size;?++i)??
  • ????{??
  • ????????memset(visit,0,sizeof?visit);??
  • ????????visit[arr[i]]?=?1;??
  • ????????for(j?=?i+1;?j?<?size;?++j)??
  • ????????{??
  • ????????????if(visit[arr[j]]?==?0)??
  • ????????????{??
  • ????????????????visit[arr[j]]?=?1;??
  • ????????????}else??
  • ????????????{??
  • ????????????????if(j-i?>?maxlen)??
  • ????????????????{??
  • ????????????????????maxlen?=?j?-?i;??
  • ????????????????????maxindex?=?i;??
  • ????????????????}??
  • ????????????????break;??
  • ????????????}??
  • ????????}??
  • ????????if((j?==?size)?&&?(j-i?>?maxlen))??
  • ????????{??
  • ????????????maxlen?=?j?-?i;??
  • ????????????maxindex?=?i;??
  • ????????}??
  • ????}??
  • ????output(arr);??
  • }??
  • ?

    2.3動態(tài)規(guī)劃求解

    ?

    動態(tài)規(guī)劃思想就是用于處理有重疊問題的求解,最大不重復(fù)子串一定是兩個相同字符夾著的一段字符串加上這個字符,如abcac這里的最大不重復(fù)子串是a夾的一段。

    當一個最長子串結(jié)束時(即遇到重復(fù)的字符),新的子串的長度是與第一個重復(fù)的字符的下標有關(guān)的,如果該下標在上一個最長子串起始位置之前,則dp[i] = dp[i-1] + 1,即上一個最長子串的起始位置也是當前最長子串的起始位置;如果該下標在上一個最長子串起始位置之后,則新的子串是從該下標之后開始的。簡短幾句話可能講不是很明白,不過好在有程序可以幫助理解,慣例下面附上程序:

    C代碼 ?
  • /*?LNRS?dp?*/??
  • int?dp[30];??
  • ???
  • void?LNRS_dp(char?*?arr,?int?size)??
  • {??
  • ????int?i,?j;??
  • ????int?last_start?=?0;?????//?上一次最長子串的起始位置??
  • ????maxlen?=?maxindex?=?0;??
  • ???
  • ????dp[0]?=?1;??
  • ????for(i?=?1;?i?<?size;?++i)??
  • ????{??
  • ????????for(j?=?i-1;?j?>=?last_start;?--j)?//?遍歷到上一次最長子串起始位置??
  • ????????{??
  • ????????????if(arr[j]?==?arr[i])??
  • ????????????{??
  • ????????????????dp[i]?=?i?-?j;??
  • ????????????????last_start?=?j+1;?//?更新last_start??
  • ????????????????break;??
  • ????????????}else?if(j?==?last_start)?//?無重復(fù)??
  • ????????????{??
  • ????????????????dp[i]?=?dp[i-1]?+?1;??
  • ????????????}??
  • ????????}??
  • ????????if(dp[i]?>?maxlen)??
  • ????????{??
  • ????????????maxlen?=?dp[i];??
  • ????????????maxindex?=?i?+?1?-?maxlen;??
  • ????????}??
  • ????}??
  • ????output(arr);??
  • }??
  • 2.4動態(tài)規(guī)劃+Hash求解

    上面動態(tài)規(guī)劃求解時間復(fù)雜度還是O(n2),主要是還是進行“回頭”查找了重復(fù)元素位置,其實,上面并不是真正的動態(tài)規(guī)劃方法,因為上面的求解過程沒有記錄有用的結(jié)果,所以可以通過記錄之前出現(xiàn)的下標來改進算法,這樣就不用每次都回去查找重復(fù)元素位置,這其實才是真正的動態(tài)規(guī)劃方法,只是記錄結(jié)果是用的Hash,這樣的時間復(fù)雜度就是O(n)。

    ?

    C代碼 ?
  • /*?LNRS?dp?+?hash?記錄下標?*/??
  • void?LNRS_dp_hash(char?*?arr,?int?size)??
  • {??
  • ????memset(visit,?-1,?sizeof?visit);??
  • ????memset(dp,?0,?sizeof?dp);??
  • ????maxlen?=?maxindex?=?0;??
  • ????dp[0]?=?1;??
  • ????visit[arr[0]]?=?0;??
  • ????int?last_start?=?0;??
  • ???
  • ????for(int?i?=?1;?i?<?size;?++i)??
  • ????{??
  • ????????if(visit[arr[i]]?==?-1)??
  • ????????{??
  • ????????????dp[i]?=?dp[i-1]?+?1;??
  • ????????????visit[arr[i]]?=?i;?/*?記錄字符下標?*/??
  • ????????}else??
  • ????????{??
  • ????????????if(last_start?<=?visit[arr[i]])??
  • ????????????{??
  • ????????????????dp[i]?=?i?-?visit[arr[i]];??
  • ????????????????last_start?=?visit[arr[i]]?+?1;??
  • ????????????????visit[arr[i]]?=?i;?/*?更新最近重復(fù)位置?*/??
  • ????????????}else??
  • ????????????{??
  • ????????????????dp[i]?=?dp[i-1]?+?1;??
  • ????????????}??
  • ???
  • ????????}??
  • ????????if(dp[i]?>?maxlen)??
  • ????????{??
  • ????????????maxlen?=?dp[i];??
  • ????????????maxindex?=?i?+?1?-?maxlen;??
  • ????????}??
  • ????}??
  • ????output(arr);??
  • }??
  • ?

    ?進一步優(yōu)化

    上面的程序因為輔助的空間多了,是不是還能優(yōu)化,仔細看DP最優(yōu)子問題解的更新方程:

    dp[i] = dp[i-1] + 1;

    dp[i-1]不就是更新dp[i]當前的最優(yōu)解么?這與最大子數(shù)組和問題的優(yōu)化幾乎同出一轍,我們不需要O(n)的輔助空間去存儲子問題的最優(yōu)解,而只需O(1)的空間就可以了,至此,我們找到了時間復(fù)雜度O(N),輔助空間為O(1)(一個額外變量與256大小的散列表)的算法,代碼如下:

    注意:當前最長子串的構(gòu)成是與上一次最長子串相關(guān)的,故要記錄上一次最長子串的起始位置!

    ?

    C代碼 ?
  • /*?LNRS?dp?+?hash?優(yōu)化?*/??
  • void?LNRS_dp_hash_impro(char?*?arr,?int?size)??
  • {??
  • ????memset(visit,?-1,?sizeof?visit);??
  • ????maxlen?=?maxindex?=?0;??
  • ????visit[arr[0]]?=?0;??
  • ????int?curlen?=?1;??
  • ????int?last_start?=?0;??
  • ???
  • ????for(int?i?=?1;?i?<?size;?++i)??
  • ????{??
  • ????????if(visit[arr[i]]?==?-1)??
  • ????????{??
  • ????????????++curlen;??
  • ????????????visit[arr[i]]?=?i;?/*?記錄字符下標?*/??
  • ????????}else??
  • ????????{??
  • ????????????if(last_start?<=?visit[arr[i]])??
  • ????????????{??
  • ????????????????curlen?=?i?-?visit[arr[i]];??
  • ????????????????last_start?=?visit[arr[i]]?+?1;??
  • ????????????????visit[arr[i]]?=?i;?/*?更新最近重復(fù)位置?*/??
  • ????????????}else??
  • ????????????{??
  • ????????????????++curlen;??
  • ????????????}??
  • ????????}??
  • ????????if(curlen?>?maxlen)??
  • ????????{??
  • ????????????maxlen?=?curlen;??
  • ????????????maxindex?=?i?+?1?-?maxlen;??
  • ????????}??
  • ????}??
  • ????output(arr);??
  • }??
  • ?

    ?最后在給出測試用例

    ?

    C代碼 ?
  • /*?輸出LNRS?*/??
  • void?output(char?*?arr)??
  • {??
  • ????if(maxlen?==?0)??
  • ????{??
  • ????????printf("NO?LNRS\n");??
  • ????}??
  • ????printf("The?len?of?LNRS?is?%d\n",maxlen);??
  • ???
  • ????int?i?=?maxindex;??
  • ????while(maxlen--)??
  • ????{??
  • ????????printf("%c",arr[i++]);??
  • ????}??
  • ????printf("\n");??
  • }??
  • ???
  • void?main()??
  • {??
  • ?????char?arr[]?=?"abcaacdeabacdefg";??
  • ???
  • ?????/*?LNRS?基本算法?*/??
  • ?????LNRS_hash(arr,strlen(arr));??
  • ???
  • ?????/*?LNRS?dp?*/??
  • ?????LNRS_dp(arr,strlen(arr));??
  • ???
  • ?????/*?LNRS?dp?+?hash?記錄下標?*/??
  • ?????LNRS_dp_hash(arr,strlen(arr));??
  • ???
  • ?????/*?LNRS?dp?+?hash?優(yōu)化方案?*/??
  • ?????LNRS_dp_hash_impro(arr,strlen(arr));??
  • }??
  • ?╝④

    ?

    §3 小結(jié)

    這篇文章把字符串最長重復(fù)子串和最長不重復(fù)子串的求解方法,能有了一定的認識和理解,基本可以掌握這些方法。如果你有任何建議或者批評和補充,請留言指出,不勝感激,更多參考請移步互聯(lián)網(wǎng)。

    ?

    ?

    參考:

    勇幸|Thinking: http://www.ahathinking.com/archives/121.html

    ②huang12315: http://blog.csdn.net/huang12315/article/details/6455090

    ③unixfy: http://www.cppblog.com/unixfy/archive/2011/05/23/146986.html

    勇幸|Thinking: http://www.ahathinking.com/archives/123.html

    ?


    總結(jié)

    以上是生活随笔為你收集整理的最长重复子串和最长不重复子串求解的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。