【算法】Substring with Concatenation of All Words 所有單詞鏈接而成的子串

題目

You are given a string, s, and a list of words, words, that are all of the same length. Find all starting indices of substring(s) in s that is a concatenation of each word in words exactly once and without any intervening characters.

Example 1:

Input:
s = "barfoothefoobarman",
words = ["foo","bar"]
Output: [0,9]
Explanation: Substrings starting at index 0 and 9 are "barfoo" and "foobar" respectively.
The output order does not matter, returning [9,0] is fine too.
Example 2:

Input:
s = "wordgoodgoodgoodbestword",
words = ["word","good","best","word"]
Output: []

給出一個字符串 s ,和一個單詞數(shù)組 words,words 中所有的單詞長度相同底扳,找出 s 中仅偎,由 words 所有單詞拼接而成的子串的起始索引摄杂。

解題思路

因為 words 中所有的單詞長度是相同的,所以可以考慮以固定的單詞長度為切割,遍歷比較每個單詞:

  1. 用 words 創(chuàng)建 map,key 為單詞昂验,value 為單詞的數(shù)量。
  2. s 長度 sLen,單詞個數(shù) wCount既琴,單詞長度 wLen占婉,存儲結(jié)果數(shù)組 result。
  3. 由于是遍歷比較單詞甫恩,所以兩層遍歷逆济,外層的遍歷 i 是 0 到 wLen-1,里層遍歷 j 為 i 到 sLen-wLen磺箕,每次增長 wLen奖慌。
  4. 外層遍歷:將 left 索引移動到 i ,總匹配成功單詞數(shù)量 count = 0滞磺,創(chuàng)建單詞匹配池 pairPool升薯,key 為單詞,匹配數(shù)量 value 為 0 击困。
  5. 里層遍歷:
    • 獲取 s 中的單詞 subStr涎劈,若匹配成功,則 pairPool 中對應(yīng)匹配數(shù)量 +1阅茶,總匹配成功單詞數(shù)量 +1
    • 匹配成功之后蛛枚,如果 subStr 匹配成功數(shù)量大于 words 中的數(shù)量,則說明 subStr 匹配過多脸哀,需要從 left 開始逐一拋棄單詞蹦浦,直至subStr 匹配數(shù)量與 words 中的數(shù)量一致
    • 如果 總匹配成功單詞數(shù)量 count 等于 wCount,則說明在該次比較中撞蜂,words 的所有單詞均匹配成功盲镶,left 即為一個結(jié)果,將 left 加入到 result中蝌诡,count -1溉贿,left 右移 wLen
    • 如果 subStr 匹配失敗,則拋棄之前的匹配數(shù)據(jù)浦旱,重置 pairPool宇色、count,將left 重置到 j + wLen處
  6. 兩層遍歷結(jié)束后颁湖,返回結(jié)果 result

代碼實現(xiàn)

func findSubstring(_ s: String, _ words: [String]) -> [Int] {
        //如果 s 或者 words 為空宣蠕,則返回 []
        if s.isEmpty || words.count<=0 {
            return []
        }
        //聲明 s 長度 sLen,單詞個數(shù) wCount甥捺,單詞長度 wLen
        let sLen = s.count, wCount = words.count, wLen = words.first!.count
        //聲明 result 存儲結(jié)果用
        var result = [Int]()
        // map 用于存儲 words 中每個單詞的個數(shù)
        var map = [String:Int]()
        // pairPool1 用于重新初始化匹配單詞池
        var pairPool1 = [String:Int]()
        // 配置 map 和 pairPool1
        for word in words {
            if map.keys.contains(word) {
                map[word]! += 1
            }else{
                map[word] = 1
                pairPool1[word] = 0
            }
        }
        // 遍歷從 0 到 wLen-1
        for i in 0 ..< wLen {
            // left 為目標子串的左索引抢蚀, count 為已經(jīng)匹配成功的單詞個數(shù)
            var left = i, count = 0
            //將pairPool1 復(fù)制給 pairPool 進行初始化,pairPool 用以存儲每個單詞已經(jīng)匹配的個數(shù)
            var pairPool = pairPool1
            // 從 i 開始涎永,wlen 為長度思币,sLen=wLen 為尾鹿响,遍歷每個單詞
            for j in stride(from: i, through: sLen-wLen, by: wLen) {
                //獲取 s 中 j 索引處的單詞 subStr
                let jIndex = s.index(s.startIndex, offsetBy: j)
                let subStr = String(s[jIndex ..< s.index(jIndex, offsetBy: wLen)])
                //如果 words 中含有該單詞羡微,則單詞匹配成功
                if map.keys.contains(subStr) {
                    //單詞匹配成功谷饿,在匹配池中,subStr 的匹配數(shù)量 +1 妈倔,總匹配數(shù)量 count +1
                    pairPool[subStr]! += 1
                    count += 1
                    //如果 s 中 subStr 匹配數(shù)量大于 words 中的數(shù)量博投,則從 left 開始逐一拋棄單詞
                    //直至 s 中 subStr 匹配數(shù)量等于 words 中的數(shù)量
                    while pairPool[subStr]! > map[subStr]! {
                        //獲取 left 索引處單詞 lSubStr
                        let lIndex = s.index(s.startIndex, offsetBy: left)
                        let lSubStr = String(s[lIndex ..< s.index(lIndex, offsetBy: wLen)])
                        // lSubStr 匹配成功數(shù)量 -1
                        pairPool[lSubStr]! -= 1
                        //總匹配成功單詞數(shù)量 count -1
                        count -= 1
                        // left 右移 wLen 位
                        left += wLen
                    }
                    //如果 count 等于 wCount,則說明在該次比較中盯蝴,words 的所有單詞均匹配成功毅哗,left 即為一個結(jié)果
                    if count == wCount {
                        // words 中所有單詞匹配成功,將結(jié)果 left 添加到 result 中
                        result.append(left)
                        //獲取 left 索引處單詞 lSubStr
                        let lIndex = s.index(s.startIndex, offsetBy: left)
                        let lSubStr = String(s[lIndex ..< s.index(lIndex, offsetBy: wLen)])
                        // lSubStr 匹配成功數(shù)量 -1
                        pairPool[lSubStr]! -= 1
                        //總匹配成功單詞數(shù)量 count -1
                        count -= 1
                        // left 右移 wLen 位
                        left += wLen
                    }
                }else{
                    //若 words 中沒有該單詞捧挺,則說明匹配失敗,拋棄之前的匹配數(shù)據(jù),重置 pairPool邑滨、count捷泞,將left 重置到 j + wLen處
                    pairPool = pairPool1
                    count = 0
                    left = j + wLen
                }
            }
        }
        //返回結(jié)果
        return result
    }

代碼地址:https://github.com/sinianshou/EGSwiftLearning

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市黑竞,隨后出現(xiàn)的幾起案子捕发,更是在濱河造成了極大的恐慌,老刑警劉巖很魂,帶你破解...
    沈念sama閱讀 211,817評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件扎酷,死亡現(xiàn)場離奇詭異,居然都是意外死亡遏匆,警方通過查閱死者的電腦和手機法挨,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,329評論 3 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來幅聘,“玉大人凡纳,你說我怎么就攤上這事『芭” “怎么了惫企?”我有些...
    開封第一講書人閱讀 157,354評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長陵叽。 經(jīng)常有香客問我狞尔,道長,這世上最難降的妖魔是什么巩掺? 我笑而不...
    開封第一講書人閱讀 56,498評論 1 284
  • 正文 為了忘掉前任偏序,我火速辦了婚禮,結(jié)果婚禮上胖替,老公的妹妹穿的比我還像新娘研儒。我一直安慰自己豫缨,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 65,600評論 6 386
  • 文/花漫 我一把揭開白布端朵。 她就那樣靜靜地躺著好芭,像睡著了一般。 火紅的嫁衣襯著肌膚如雪冲呢。 梳的紋絲不亂的頭發(fā)上舍败,一...
    開封第一講書人閱讀 49,829評論 1 290
  • 那天,我揣著相機與錄音敬拓,去河邊找鬼邻薯。 笑死,一個胖子當(dāng)著我的面吹牛乘凸,可吹牛的內(nèi)容都是我干的厕诡。 我是一名探鬼主播,決...
    沈念sama閱讀 38,979評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼营勤,長吁一口氣:“原來是場噩夢啊……” “哼灵嫌!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起冀偶,我...
    開封第一講書人閱讀 37,722評論 0 266
  • 序言:老撾萬榮一對情侶失蹤醒第,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后进鸠,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體稠曼,經(jīng)...
    沈念sama閱讀 44,189評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,519評論 2 327
  • 正文 我和宋清朗相戀三年客年,在試婚紗的時候發(fā)現(xiàn)自己被綠了霞幅。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,654評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡量瓜,死狀恐怖司恳,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情绍傲,我是刑警寧澤扔傅,帶...
    沈念sama閱讀 34,329評論 4 330
  • 正文 年R本政府宣布,位于F島的核電站烫饼,受9級特大地震影響猎塞,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜杠纵,卻給世界環(huán)境...
    茶點故事閱讀 39,940評論 3 313
  • 文/蒙蒙 一荠耽、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧比藻,春花似錦铝量、人聲如沸倘屹。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,762評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽纽匙。三九已至,卻和暖如春插爹,著一層夾襖步出監(jiān)牢的瞬間哄辣,已是汗流浹背请梢。 一陣腳步聲響...
    開封第一講書人閱讀 31,993評論 1 266
  • 我被黑心中介騙來泰國打工赠尾, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人毅弧。 一個月前我還...
    沈念sama閱讀 46,382評論 2 360
  • 正文 我出身青樓气嫁,卻偏偏與公主長得像,于是被迫代替她去往敵國和親够坐。 傳聞我的和親對象是個殘疾皇子寸宵,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,543評論 2 349

推薦閱讀更多精彩內(nèi)容