1. 程式人生 > 實用技巧 >leetcode [1520. 最多的不重疊子字串]

leetcode [1520. 最多的不重疊子字串]

(https://leetcode-cn.com/problems/maximum-number-of-non-overlapping-substrings/)

如果給我們所有的符合題目條件的子字串(條件一:這些字串之間互不重疊,也就是說對於任意兩個子字串 s[i..j] 和 s[k..l] ,要麼 j < k 要麼 i > l 條件二:如果一個子字串包含字元 char ,那麼 s 中所有 char 字元都應該在這個子字串中),那麼這題最起碼對我來說我覺得會好寫的多,就是給你一些子字串,問你不重疊的情況下,最多可以選幾個子字串,我們就可以用動態規劃去解,可以在程式碼中看。下面關鍵的是怎麼去求符合條件的子字串

先放程式碼:

		//求解符合條件的子字串
		int n = s.size(),p[26],q[26];//p放字元c第一次出現的位置,q放字元c最後出現的位置
        for(int i = 0; i < 26; i++) p[i] = -1,q[i] = n; //初始化
        for(int i = 0; i < n; i++){
            if(p[s[i]-'a'] != -1) continue;
            p[s[i]-'a'] = i;
        }//求字元第一次出現的位置
        for(int i = n-1; i >= 0; i--){
            if(q[s[i]-'a'] != n) continue;
            q[s[i]-'a'] = i;
        }//求字元最後一次出現的位置
        vector<pair<int,int>> word; //存放所有符合條件的子字串(用起始位置和終止位置來表示)
        for(int c = 0; c < 26; c++){
            if(p[c] == -1) continue;
            int i = p[c],j = q[c];
            while(i <= q[c] || j >= p[c]){
                while(i <= q[c]){
                    p[c] = min(p[c],p[s[i]-'a']);
                    q[c] = max(q[c],q[s[i]-'a']);
                    i++;
                }
                while(j >= p[c]){
                    p[c] = min(p[c],p[s[j]-'a']);
                    q[c] = max(q[c],q[s[j]-'a']);
                    j--;
                }
            } //關鍵程式碼
            word.push_back(make_pair(p[c],q[c]));
        }

關鍵程式碼解釋:如圖,剛開始的字串的範圍是i~j ,但i-j裡面可能會存在一些字元,它的範圍會超出了i-j,i往右走的過程中,字串的範圍可能會逐漸變大,我們就不斷迴圈,讓i一直往右走,j一直往左走,同時不斷更新子字串的左右端點,知道i和j走不動為止

bool cmp(pair<int,int> a,pair<int,int> b) {return a.first < b.first;}
class Solution {
public:
    vector<string> maxNumOfSubstrings(string s) {
        int n = s.size(),p[26],q[26];
        for(int i = 0; i < 26; i++) p[i] = -1,q[i] = n;
        for(int i = 0; i < n; i++){
            if(p[s[i]-'a'] != -1) continue;
            p[s[i]-'a'] = i;
        }
        for(int i = n-1; i >= 0; i--){
            if(q[s[i]-'a'] != n) continue;
            q[s[i]-'a'] = i;
        }
        vector<pair<int,int>> word;
        for(int c = 0; c < 26; c++){
            if(p[c] == -1) continue;
            int i = p[c],j = q[c];
            while(i <= q[c] || j >= p[c]){
                while(i <= q[c]){
                    p[c] = min(p[c],p[s[i]-'a']);
                    q[c] = max(q[c],q[s[i]-'a']);
                    i++;
                }
                while(j >= p[c]){
                    p[c] = min(p[c],p[s[j]-'a']);
                    q[c] = max(q[c],q[s[j]-'a']);
                    j--;
                }
            }
            word.push_back(make_pair(p[c],q[c]));
        }
        sort(word.begin(),word.end(),cmp);
        //dp過程
        vector<int> dp(word.size(),1); //dp[i] 表示最後取第i個子字串所能得到的最大數量
        vector<int> pre(word.size(),-1);//表示pre[i]表示dp[i]是從下標pre[j]轉移過來的,最後用於ans的生成
        vector<int> len(word.size());//len[i] 表示 dp[i]數量的子字串的長度和
        int maxx = -1,max_idx;//maxx表示最大數量,max_idx表示最大數量對應的dp下標
        for(int i = 0; i < dp.size(); i++){
            int val = 0,cnt_idx;
            for(int j = 0; j < i; j++){
                if(word[j].second >= word[i].first) continue;
                if(dp[j] > val){
                    val = dp[j];
                    cnt_idx = j;
                }
                else if(dp[j] == val){
                    if(len[j] <= len[cnt_idx]) val = dp[j],cnt_idx = j;
                }
            }
            //答案更新
            dp[i] += val;//更新dp
            if(val == 0) len[i] = word[i].second-word[i].first+1;//更新len
            else len[i] = len[cnt_idx]+word[i].second-word[i].first+1,pre[i] = cnt_idx;
            
            if(maxx < dp[i]) maxx = dp[i],max_idx = i;
            else if(maxx == dp[i]){
                int cmp_idx1 = max_idx,cmp_idx2 = i;
                int len1 = 0,len2 = 0;
                while(cmp_idx1 != -1) len1 += len[cmp_idx1],cmp_idx1 = pre[cmp_idx1];
                while(cmp_idx2 != -1) len2 += len[cmp_idx2],cmp_idx2 = pre[cmp_idx2];
                if(len2 < len1) max_idx = i;
            }
        }
		//生成ans陣列
        vector<string> ans;
        while(max_idx != -1){
            string str = s.substr(word[max_idx].first,word[max_idx].second-word[max_idx].first+1);
            ans.push_back(str);
            max_idx = pre[max_idx];
        }
        return ans;
    }
};

最後的dp過程可能我寫複雜了,但我不想改了hhhh,這題還是挺細節的,需要好好想想