LeetCode 30串联所有单词的子串31下一个排列
標(biāo)題
- 串聯(lián)所有單詞得字串
- 下一個(gè)排列
維護(hù)真的不易,如有幫助還請(qǐng)點(diǎn)贊關(guān)注,關(guān)注公眾號(hào)bigsai回復(fù)進(jìn)群即可加入打卡。
串聯(lián)所有單詞得字串
題目描述:
給定一個(gè)字符串 s 和一些長(zhǎng)度相同的單詞 words。找出 s 中恰好可以由 words 中所有單詞串聯(lián)形成的子串的起始位置。
注意子串要與 words 中的單詞完全匹配,中間不能有其他字符,但不需要考慮 words 中單詞串聯(lián)的順序。
示例 1:
輸入:
s = “barfoothefoobarman”,
words = [“foo”,“bar”]
輸出:[0,9]
解釋:
從索引 0 和 9 開始的子串分別是 “barfoo” 和 “foobar” 。
輸出的順序不重要, [9,0] 也是有效答案。
示例 2:
輸入:
s = “wordgoodgoodgoodbestword”,
words = [“word”,“good”,“best”,“word”]
輸出:[]
分析:
這題講真還是挺有技巧和方案的,刷這道題也花了不少心思,需要考慮的點(diǎn)也稍微多一點(diǎn)。題意就是要找到字符串s的某個(gè)字串可以由words中所有單詞組成。返回滿足匹配s子串的首位編號(hào)。
遞歸法:
從處理的方式上理論是可以使用遞歸的,但是由于層數(shù)太多并且個(gè)別比較特殊的數(shù)據(jù)可能導(dǎo)致爆棧TL。這里就有個(gè)教訓(xùn):
主要當(dāng)時(shí)沒(méi)仔細(xì)讀題,沒(méi)有在意每個(gè)單詞長(zhǎng)度都相同所以以為是搜索題,后來(lái)仔細(xì)看題之后才發(fā)現(xiàn)問(wèn)題。
普通哈希法(滑動(dòng)窗口)
對(duì)于有些人叫啥滑動(dòng)窗口啥稀奇古怪的漂亮名稱,這里我就簡(jiǎn)稱為Hash法。如何去分析和處理這個(gè)問(wèn)題呢?我們可以看看一些重要的條件:
- words中所有單詞長(zhǎng)度都相同
- 必須使用所有words中的單詞一次
也就是說(shuō)在進(jìn)行匹配的時(shí)候可以根據(jù)單詞進(jìn)行匹配。
但每個(gè)字符串進(jìn)行判斷的時(shí)候,可以進(jìn)行分割成若干單詞數(shù)判斷。
用兩個(gè)HashMap儲(chǔ)存單詞數(shù)即可,存儲(chǔ)途中進(jìn)行判斷如果有不滿足直接停止。如果能跑到最后說(shuō)明可以添加這個(gè)標(biāo)記。
具體實(shí)現(xiàn)的代碼為:
public List<Integer> findSubstring(String s, String[] words) {List<Integer>value=new ArrayList<Integer>();Map<String, Integer>map=new HashMap<String, Integer>();for(int i=0;i<words.length;i++){int num=map.getOrDefault(words[i], 0);map.put(words[i], num+1);}int wordlen=words[0].length();int len=words[0].length()*words.length;StringBuilder sBuilder=new StringBuilder(" ");sBuilder.append(s.substring(0, len-1));for(int i=0;i<s.length()-len+1;i++){sBuilder.deleteCharAt(0);sBuilder.append(s.charAt(i+len-1));int num=0;//統(tǒng)計(jì)總共滿足的單詞數(shù)量Map<String, Integer>map2=new HashMap<String, Integer>();//map2.putAll(map);int index=0;while (index<len) {String team=sBuilder.substring(index,index+wordlen);int number=map2.getOrDefault(team, 0);//次數(shù)map2.put(team, number+1);if(number+1>map.getOrDefault(team, 0))break;index+=wordlen;}if(index==len)value.add(i);}return value; }Hash滑動(dòng)窗口優(yōu)化
可以發(fā)現(xiàn)在上面所涉及的的滑動(dòng)處理中每次都需要重新計(jì)算當(dāng)前字符串的HashMap情況并重新匹配。這樣就有很多已經(jīng)匹配的情況就浪費(fèi)了。
所以就需要優(yōu)化來(lái)去掉重復(fù)的計(jì)算,首先要將字符串分成單詞長(zhǎng)度的組數(shù)來(lái)分別計(jì)算。
然后每組在詳細(xì)進(jìn)行的時(shí)候需要兩個(gè)指針動(dòng)態(tài)向右表示區(qū)間匹配。而Map通常不需要每次都刷新可以重新利用。一個(gè)坐標(biāo)j代表開頭一個(gè)index表示當(dāng)前結(jié)尾。
你可能會(huì)遇到以下幾種情況:
- 遇到新單詞不存在,此時(shí)j和index都移動(dòng)到單詞后重新開始,且儲(chǔ)存的動(dòng)態(tài)map需要clear;
- 遇到的單詞存在,但是多了,需要將j右移一直到消除這個(gè)多余單詞,同時(shí)修改動(dòng)態(tài)map。
- 正常情況,疊加匹配更新index和map。
上面步驟完成之后,如果j+len==index那么就說(shuō)明完成匹配,添加此個(gè)編號(hào)。但在此同時(shí),可以判斷下個(gè)單詞是否與當(dāng)前首單詞相等,如果相等直接更新對(duì)應(yīng)j和index順便加入結(jié)果集中,不過(guò)不需要更新動(dòng)態(tài)的map。
有了上述優(yōu)化的思路,就可以碼代碼了。注意細(xì)節(jié)實(shí)現(xiàn),前開后閉等等,具體代碼為:
public List<Integer> findSubstring(String s, String[] words) {List<Integer>value=new ArrayList<Integer>();//返回的結(jié)果Map<String, Integer>map=new HashMap<String, Integer>();//統(tǒng)計(jì)單詞個(gè)數(shù)for(String team:words)//進(jìn)行統(tǒng)計(jì){int num=map.getOrDefault(team, 0);map.put(team, num+1);}int wordlen=words[0].length();//單個(gè)單詞的長(zhǎng)度int len=words[0].length()*words.length;//總長(zhǎng)度for(int i=0;i<wordlen;i++)//分組分別進(jìn)行{int j=i,index=j;Map<String, Integer>map2=new HashMap<String, Integer>();while (j<=s.length()-len&&index+wordlen<=s.length()) {String word=s.substring(index,index+wordlen);int num=map2.getOrDefault(word, 0);map2.put(word, num+1);if(!map.containsKey(word))//不包含該元素,直接跳過(guò){j=index+wordlen;map2.clear(); }else if(map.get(word)<num+1)//元素存在但次數(shù)過(guò)多{String teamstr="";while (!(teamstr=s.substring(j,j+wordlen)).equals(word)) {//找到第一個(gè)不相等得map2.put(teamstr, map2.get(teamstr)-1);j+=wordlen;}map2.put(teamstr, map2.get(teamstr)-1);j+=wordlen;}index+=wordlen;if(index==j+len){value.add(j);while (index+wordlen<=s.length()&&s.substring(j, j+wordlen).equals(s.substring(index, index+wordlen))) {value.add(j+wordlen);j+=wordlen;index+=wordlen;} String teamstr=s.substring(j,j+wordlen);map2.put(teamstr, map2.get(teamstr)-1);j+=wordlen;}}}return value; }下一個(gè)排列
題目描述:
實(shí)現(xiàn)獲取下一個(gè)排列的函數(shù),算法需要將給定數(shù)字序列重新排列成字典序中下一個(gè)更大的排列。
如果不存在下一個(gè)更大的排列,則將數(shù)字重新排列成最小的排列(即升序排列)。
必須原地修改,只允許使用額外常數(shù)空間。
以下是一些例子,輸入位于左側(cè)列,其相應(yīng)輸出位于右側(cè)列。
1,2,3 → 1,3,2
3,2,1 → 1,2,3
1,1,5 → 1,5,1
分析:
是不是和全排列有點(diǎn)像,但是又不是全排列。是需要找到下一個(gè)字典序。
分析數(shù)列你會(huì)發(fā)現(xiàn)以下兩個(gè)規(guī)律:
首先從右往左交換第一個(gè)正序:
- 例如1 2 3 5 4 交換3和4成為1 2 4 5 3.
其次根據(jù)交換的區(qū)間內(nèi),從右向左(雙重循環(huán))交換逆序?qū)?#xff1a;
- 例如上述變成1 2 4 3 5;
具體實(shí)現(xiàn)的時(shí)候,注意位置編號(hào)等問(wèn)題。具體代碼為:
public void nextPermutation(int[] nums) {boolean jud=false;int i,j=0;for( i=nums.length-2;i>=0;i--){for( j=nums.length-1;j>i;j--){if(!jud&&nums[i]<nums[j]){int team=nums[i];nums[i]=nums[j];nums[j]=team;jud=true;break;}}if(jud)break;}if(jud)for(int k=nums.length-1;k>i;k--){for(int m=k-1;m>i;m--){if(nums[k]<nums[m]){int team=nums[k];nums[k]=nums[m];nums[m]=team;}}}int team;if(!jud)for( i=0;i<nums.length/2;i++){team=nums[i];nums[i]=nums[nums.length-1-i];nums[nums.length-1-i]=team;}}
好啦,本次打卡就到這里啦,更多精彩歡迎關(guān)注bigsai,回復(fù)進(jìn)群加入打卡,回復(fù)bigsai獲取珍藏pdf資源。
總結(jié)
以上是生活随笔為你收集整理的LeetCode 30串联所有单词的子串31下一个排列的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: LeetCode 27移除元素28实现s
- 下一篇: MongoDB从立地到成佛(介绍、安装、