• leetcode 字符串


     动态规划问题还不是很会,需要再加深

    解法1:

    思路:双指针。

    两个方法很重要!

    isalnum(char c):判断字符变量c是否为字母或数字,若是则返回非零,否则返回零。 tolower(char c):把字母字符转换成小写,非字母字符不做出处理。

    复制代码
    class Solution {
    public:
        bool isPalindrome(string s) {
            int lefts=0;
            int rights=s.size()-1;
            while(lefts<rights){
                while(!isalnum(s[lefts])){
                    lefts++;
                    if(lefts>=rights)
                        return true;
                }
                while(!isalnum(s[rights])){
                    rights--;
                    if(lefts>rights){
                        return false;
                    }
                }
                if(tolower(s[lefts])!=tolower(s[rights]))
                    return false;
                lefts++;
                rights--;
            }
            return true;
        }
    };
    复制代码

    解法1: 

    复制代码
    class Solution {
    public:
        int lengthOfLastWord(string s) {
            int cnt = 0;
            int i = s.size()-1;
            while(i>=0 && s[i]==' '){
                i--;
            }
            while(i>=0 && s[i]!=' '){
                i--;
                cnt++;
            }
            return cnt;
        }
    };
    复制代码

    复制代码
    class Solution {
        public int lengthOfLastWord(String s) {
            String[] words = s.split(" ");
            if(words.length==0){
                return 0;
            }
            else{
                return words[words.length-1].length();
            }
        }
    }
    复制代码

     

     解法1:

    思路:

    复制代码
    class Solution {
    public:
        string addStrings(string num1, string num2) {
            string str;
            int cur = 0, i = num1.size()-1, j = num2.size()-1;
            while(i>=0 || j>=0 || cur !=0){
                if(i>=0) cur += num1[i--] - '0';
                if(j>=0) cur += num2[j--] - '0';
                str += to_string(cur % 10);
                cur /= 10;
            }
            reverse(str.begin(),str.end());
            return str;
        }
    };
    复制代码

    这个题和上个题的算法是一样的哦,只不过不是字符串了,而是变为了数组。

     

    复制代码
    class Solution {
    public:
        vector<int> addToArrayForm(vector<int>& A, int K) {
            vector<int> result;
            for(int i=A.size()-1; i>=0;--i){
                int num = A[i];
                K += num;
                int remind = K % 10;
                result.insert(result.begin(),remind);
                K /= 10;
            }
            if(K){
                while(K > 9){//如果K 比较大,那么此时还需要把K循环放进去,比如[0], K = 10000 的情况
                    int remind = K % 10;
                    result.insert(result.begin(),remind);
                    K /= 10;
                }
                result.insert(result.begin(),K);
            }
            return result;
        }
    };
    复制代码

     

    解法1:

    思路:代码中的i 变量用来记录每个子串的第一个位置。当每当有重复子串时,i就会移动。当j-i+1 > max时,max 就更新。最后返回max

    比如abcdcdefj 时就会返回5.  这里i 变量最为关键。

    复制代码
    class Solution {
    public:
        int lengthOfLongestSubstring(string s) {
            int size,i=0,j,k,max=0;
            size = s.size();
            for(j=0;j<size;++j){
                for(k=i;k<j;++k){
                    if(s[k]==s[j]){
                        i = k+1;
                        break;
                    }
                }
                if(j-i+1>max){
                    max = j-i+1;
                }
            }
            return max;
            
        }
    };
    复制代码

     解法1:

    动态规划

     思路:

    复制代码
    // 创建动态规划表格matrix
            // matrix[left][right]表示下标从left到right的子串(以下简写为s[left, right])是否是回文的
            // 若是,则值为其长度;否则,值为0
            // matrix所有元素初始化为0
    / 用left表示当前子串起始下标,right表示当前子串结束下标。left <= right。
            // 基本情形:
            // 1. left == right,此时子串只有一个字符,一定是回文的,且长度为1
            // 2. left == right - 1,此时子串有两个字符,当且仅当两个字符相同,子串是回文的,且长度为2// 迭代:从长度为3的子串开始
            // 当且仅当s[left] == s[right],且s[left + 1, right - 1]是回文的,s[left, right]是回文的
            // 换言之,若s[left] == s[right],且s[left + 1][right - 1] > 0,则s[left, right] = s[left + 1, right - 1] + 2
            // 注意要按列而不是按行遍历!
            // 原因是,在表格中,s[left + 1][right - 1]是在s[left][right]的右下角
            // 只有按列遍历,才能保证在计算s[left][right]前,s[left + 1][right - 1]已经被计算过
    复制代码

    精简版代码:

    复制代码
    class Solution {
    public:
        string longestPalindrome(string s) {
            int sLen = s.length();
            if(sLen==0) return "";
            string res = "";
            vector<vector<bool>> f(sLen,vector<bool>(sLen,false));
            int maxLen = 0;
            int currLen = 0;
            for(int i =0;i<sLen;++i){
                for(int j =0;j<=i;++j){
                    if((s[i]==s[j])&&((i-j<2)||(i>0 && f[i-1][j+1]))){
                        f[i][j] = true;
                        currLen = i-j+1;
                        if(currLen > maxLen){
                            maxLen = currLen;
                            res = s.substr(j,currLen);
                        }
                    }
                }
            }
            return res;
        }
    };
    复制代码

    解法2:中间扩散法:


     解法1:用封装好的reverse方法,注意reverse方法的使用。里面是迭代器的指针

    复制代码
    class Solution {
    public:
        string reverseStr(string s, int k) {
            string::iterator it = s.begin();
            while(it < s.end() && it+k <=s.end()){
                reverse(it,it+k);
                it = it+2*k;
            }
            if(it+k>s.end()){
                reverse(it,s.end());
            }
            return s;
        }
    };
    复制代码

    此题还未到国外LeetCode 去参考别人答案

     解法1:

    思路:hash 

    自己写的 

    复制代码
    class Solution {
    public:
        int firstUniqChar(string s) {
            unordered_map<char,int> result;
            for(auto c : s){
                result[c]++;
            }
            for(int i=0;i<s.size();++i){
                if(result[s[i]]==1){
                    return i;
                }
            }
            return -1;
        }
    };
    复制代码

     

    解法1:自己写的

    思路:挨个写出对应的循环判断,需要判断三种情况,时间复杂度O(3N)

     

    复制代码
    class Solution {
    public:
        bool detectCapitalUse(string word) {
            int i=0;
            for(;i<word.size();++i){
                if(word[i]-'A'>=32){
                    continue;
                }
                else break;
            }
            if(i==word.size()) return true;//全是小写
            int j=0;
            for(;j<word.size();++j){
                if(word[j]-'A'<32){
                    continue;
                }
                else break;
            }
            if(j==word.size()) return true; //全是大写
            int k =0;
            if(word[k]-'A'<32){
                ++k;
                for(;k<word.size();++k){
                    if(word[k]-'A'>=32){
                        continue;
                    }
                    else break;
                }
                if(k==word.size()) return true;
            }
            return false;
            
        }
    };
    复制代码

     

    解法1:思路:KMP算法。

    详情见KMP算法的日记

    复制代码
    class Solution {
    public:
        void calcKMPNextArray(const string & needle,vector<int> & next){
            next.resize(needle.size());
            //计算next数组的过程其实是needle与needle自己进行匹配
            next[0]=-1;
            for(int i=1,j=-1;i<needle.size();i++){
                while(j>=0 && needle[i]!=needle[j+1]) //第三天再次写这个算法,写成了needle[i]==needle[j].唉,感觉这本质还是没理解透彻
                    j = next[j];
                if(needle[i]==needle[j+1])//第三天再回顾这个算法,写成了needle[i]!=needle[j+1]
                    j++;
                next[i]=j;
            }
            //优化 next 数组,可以去除多余的跳跃步骤
            for(int i=0;i<needle.size();i++){
                while(next[i]>=0 && needle[i+1]==needle[next[i]+1])
                    next[i]=next[next[i]];
            }
        }
        
        int strStr(string haystack, string needle) {
            if(needle.size()<=0)
                return 0;
            vector<int> next;
            calcKMPNextArray(needle,next);
            int i,j;
            for(i=0,j=-1;i<haystack.size();i++){
                while(j>=0 && haystack[i]!=needle[j+1])
                    j = next[j];
                if(haystack[i]==needle[j+1])
                    j++;
                //注意,结束时,j 应该到达needle最后一个字符处,并没有超出
                //从上一句可以看出,最后一次 j++ 绝对不会超出needle索引范围
                if(j+1>=needle.size()) //这里必须要注意
                    return i-needle.size()+1;
            }
            return -1; 
        }
    };
    复制代码

    解法2:

    暴力解法

    复制代码
    int strStr(char * haystack, char * needle){
        int i=0,j=0,t=i;
        while(haystack[i]&&needle[j]){
            if(haystack[i]==needle[j]){
                ++i;
                ++j;
                continue;
            }
            else{
                t = t+1;
                i = t;
                j = 0;
            }
        }
        if(needle[j] == ''){
            return t;
        }
        return -1;
    
    }
    复制代码

     

    解法1

     

    JAVA 版

    复制代码
    class Solution {
        public String removeDuplicateLetters(String s) {
            int[] charsCount = new int[26];//计算26字母数量
            boolean[] visited = new boolean[26]; //标记字母是否已经入栈
            int len = s.length();
            char[] sChars = s.toCharArray();
            for(char c : sChars){
                charsCount[c - 'a']++;
            }
            Stack<Character> stack = new Stack<>();
            int index = 0;//最终字符的长度
            for(int count : charsCount){
                if(count > 0) index++;
            }
            char[] res = new char[index];
            for(int i=0;i<len;i++){
                char c = s.charAt(i);
                //有小字符的且满足其前面的字符再小字符后还有同样字符的,则出栈
                while(!stack.isEmpty() && c < stack.peek() && charsCount[stack.peek()-'a'] > 1
                     && !visited[c - 'a']){
                    Character pop = stack.pop();
                    visited[pop - 'a'] = false;
                    charsCount[pop - 'a']--;
                }
                if(visited[c - 'a']){
                    charsCount[c - 'a']--; //重复的字符根据游标往后移动,数量减一
                    continue;
                }
                stack.push(c);
                visited[c - 'a'] = true;
            }
            while(!stack.isEmpty()){
                res[--index] = stack.pop();
            }
            return String.valueOf(res);
        }
    }
    复制代码

     C++版

    思路:

    复制代码
    class Solution {
    public:
        string removeDuplicateLetters(string s) {
            unordered_map<char,int> mp;
            unordered_map<char,int> in_st;
            for(int i=0;i<s.size();++i){
                mp[s[i]] = i;//记录某个字符出现的最后位置
            }
            vector<char> st;//记录结果的栈
            for(int i=0;i<s.size();++i){
                if(in_st[s[i]]) continue;//栈中有当前遍历字符
                while(st.size() && s[i] < st.back() && mp[st.back()] > i){
                    //栈顶元素会在之后的位置出现
                    --in_st[st.back()];
                    st.pop_back();
                    //出栈并抹除记录
                }
                st.push_back(s[i]);
                ++in_st[s[i]];
                //压栈,并记录出现过
            }
            string res;
            for(auto& i : st) res += i;
            return res;
        }
    };
    复制代码

     

    解法1:

    思路1:此题可以近似看成一个求26进制的题,但是值得注意的是对于26的整数倍,如果我们不加以限制的话就会造成A0的情况出现,而题目给出的条件中是不考虑这种情况的。所以我们只需要排除这种情况对于任何26的整数倍,我们就直接先插入‘Z’,同时还要对原数进行减一的操作。因为如果不进行减一的话对于26所对应的就是AZ(其实就是为了将A0映射为Z要对A0整体减一,由于0-1不够,所以要向A借一位,所以最后就得到了Z。所以我们要插入Z之后再对原数减1。)

    复制代码
    class Solution {
    public:
        string convertToTitle(int n) {
            string res;
            int temp = 0;
            while(n != 0){
                temp = n%26;
                if(temp == 0){
                    res = 'Z' + res;
                    n--;
                }
                else
                    res = (char)(temp + 'A'-1) + res;
                n = n/26;
            }
            return res;
        }
    };
    复制代码

     

     

    参考题解:

    https://leetcode-cn.com/problems/minimum-window-substring/solution/hua-dong-chuang-kou-suan-fa-tong-yong-si-xiang-by-/

    解法:
    滑动窗口:

    复制代码
    class Solution {
    public:
        string minWindow(string s, string t) {
            //记录最短子串的开始位置和长度
            int start = 0, minLen = INT_MAX;
            int left = 0, right = 0;
            unordered_map<char,int> window;
            unordered_map<char,int> needs;
            for(char c : t) needs[c]++;
            
            int match = 0;
            while(right < s.size()){
                char c1 = s[right];
                if(needs.count(c1)){
                    window[c1]++;
                    if(window[c1] == needs[c1])
                        match++;
                }
                right++;
                
                while(match == needs.size()){
                    if(right - left < minLen){
                        //更新最小子串的位置和长度
                        start = left;
                        minLen = right - left;
                    }
                    char c2 = s[left];
                    if(needs.count(c2)){
                        window[c2]--;
                        if(window[c2] < needs[c2]){
                            match--;
                        }
                    }
                    left++;
                }
            }
            return minLen == INT_MAX ? "" : s.substr(start,minLen);
        
        }
    };
    复制代码

    优化版:

    复制代码
    class Solution {
    public:
        string minWindow(string s, string t) {
            int count[256] = { 0 };
        for (auto c : t) ++count[c];
        int len = 0, minLength = s.length();
        string res;
        for (int l = 0, r = 0; r < s.length(); ++r) {
            count[s[r]]--;
            if (count[s[r]] >= 0) ++len;
            while (len == t.length()) {
                if (r - l + 1 <= minLength) {
                    minLength = r - l + 1;
                    res = s.substr(l, r - l + 1);
                }
                count[s[l]]++;
                if (count[s[l]] > 0) --len;
                ++l;
            }
        }
        return res;
        }
    };
    复制代码

    再优化:

    复制代码
    class Solution {
    public:
        string minWindow(string s, string t) {
            int count[256] = { 0 };
            for(auto c : t) ++count[c];
            int len = 0, minLength = s.length();
            string res;
            for(int l = 0, r= 0;r<s.length();++r){
                if(--count[s[r]] >= 0) ++len;
                while(len == t.length()){
                    if(r - l + 1 <= minLength){
                        minLength = r - l + 1;
                        res = s.substr(l, r - l + 1);
                    }
                    if(++count[s[l++]] > 0) --len;
                }
            }
            return res;
        }
    };
    复制代码

     滑动窗口

    参考题解:

    https://leetcode-cn.com/problems/minimum-window-substring/solution/hua-dong-chuang-kou-suan-fa-tong-yong-si-xiang-by-/

    复制代码
    class Solution {
    public:
        vector<int> findAnagrams(string s, string p) {
            vector<int> res;
            int left = 0, right = 0;
            unordered_map<char,int> needs;
            unordered_map<char,int> window;
            for(char c : p) needs[c]++;
            int match = 0;
            while(right < s.size()){
                char c1 = s[right];
                if(needs.count(c1)){
                    window[c1]++;
                    if(window[c1] == needs[c1])
                        match++;
                }
                right++;
                
                while(match == needs.size()){
                    //如果window 的大小合适
                    //就把其实索引left 加入结果
                    if(right - left == p.size())
                        res.push_back(left);
                    char c2 = s[left];
                    if(needs.count(c2)){
                        window[c2]--;
                        if(window[c2] < needs[c2])
                            match--;
                    }
                    left++;
                }
            }
            return res;
        }
    };
    复制代码

    思路:滑动窗口

    需要注意的是,因为我们要求的是最长子串,所以需要在每次移动 right 增大窗口时更新 res,而不是像之前的题目在移动 left 缩小窗口时更新 res。

     

    复制代码
    class Solution {
    public:
        int lengthOfLongestSubstring(string s) {
            int left = 0, right = 0;
            unordered_map<char,int> window;
            int res = 0;//记录最长长度
            while(right < s.size()){
                char c1 = s[right];
                window[c1]++;
                right++;
                //如果window中出现重复字符
                //开始移动left缩小窗口
                while(window[c1] > 1){
                    char c2 = s[left];
                    window[c2]--;
                    left++;
                }
                res = max(res,right - left);
            }
            return res;
        }
    };
    复制代码

    其中 window 的数据类型可以视具体情况而定,比如上述题目都使用哈希表充当计数器,当然你也可以用一个数组实现同样效果,因为我们只处理英文字母。

    稍微麻烦的地方就是这个 valid 条件,为了实现这个条件的实时更新,我们可能会写很多代码。比如前两道题,看起来解法篇幅那么长,实际上思想还是很简单,只是大多数代码都在处理这个问题而已。

    作者:labuladong
    链接:https://leetcode-cn.com/problems/minimum-window-substring/solution/hua-dong-chuang-kou-suan-fa-tong-yong-si-xiang-by-/
    来源:力扣(LeetCode)
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。


     

    解法1:

    JAVA

    复制代码
    class Solution {
        public int longestSubstring(String s, int k) {
            if(k < 2) return s.length();
            return process(s.toCharArray(),k,0,s.length()-1);
        }
        private int process(char[] s,int k,int lo,int hi){
            if(hi - lo + 1 < k) return 0;
            int[] cnts = new int[26];
            for(int i = lo;i <= hi;++i) cnts[s[i]-'a']++;
            while(hi - lo + 1 >= k && cnts[s[lo]-'a'] < k) lo++;
            while(hi - lo + 1 >= k && cnts[s[hi]-'a'] < k) hi--;
            if(hi - lo + 1 < k) return 0;
            for(int i = lo;i<=hi;++i){
                if(cnts[s[i]-'a'] < k) return Math.max(process(s,k,lo,i-1),process(s,k,i+1,hi));
            }
            return hi - lo + 1;
        }
    }
    复制代码

    解法2

    C++ 递归+分治

    复制代码
    class Solution {
    public:
        int longestSubstring(string s, int k) {
            int result = 0;
            helper(s,k,result);
            return result;
        }
        void helper(string s,int k,int &result){
            unordered_map<char,int> mmap;
            for(int i=0;i<s.length();i++)
                mmap[s[i]]++;
            for(auto i:mmap){
                if(i.second<k){
                    int char_index = s.find(i.first);
                    helper(s.substr(0,char_index),k,result);
                    helper(s.substr(char_index+1,s.length()),k,result);
                    return;
                }
            }
            result = result>s.length()?result:s.length();
            return;
        }
    };
    复制代码

     分治 + 递归

    复制代码
    class Solution {
    public:
        int longestSubstring(string s, int k) {
            if(k <= 1) return s.size();
            if(s.empty() || s.size() < k) return 0;
            vector<int> hash(128,0);
            for(char c : s) ++hash[c];
            int i = 0;
            while(i < s.size() && hash[s[i]] >= k) ++i;
            if(i == s.size()) return s.size();
            int l = longestSubstring(s.substr(0,i),k);
            while(i < s.size() && hash[s[i]] < k) ++i;
            int r = longestSubstring(s.substr(i),k);
            
            return max(l,r);
        }
    };
    复制代码

    解法1:

    思路:

    四种情况:

    1. 数组里有空字符串,并且数组里还有自己就是回文的字符串,每出现一个可与空字符串组成两对。
    2. 如果自己的翻转后的字符串也在数组里,可以组成一对,注意翻转后不能是自己。
    3. 如果某个字符串能找到一个分割点,分割点前的部分是回文,后半部分翻转后也在数组里,可组成一对。
    4. 把3反过来,如果后部分是回文,前半部分翻转后在数组里,可组成一对。

     

    复制代码
    class Solution {
    public:
        bool f(string& s,int left,int right){
            while(left < right){
                if(s[left++] != s[right--]) return false;
            }
            return true;
        }
        vector<vector<int>> palindromePairs(vector<string>& words) {
            unordered_map<string,int> m;
            set<int> s;
            int n = words.size();
            for(int i=0;i<n;++i){
                m[words[i]] = i;//记录哈希
                s.insert(words[i].size());//获取每段字符串的长度
            }
            vector<vector<int>> res;//结果
            for(int i = 0;i<n;++i){
                string tmp = words[i];
                reverse(tmp.begin(),tmp.end());
                if(m.count(tmp) && m[tmp] != i){//如果哈希表里有目标字符串且不是自己
                    res.push_back({m[tmp],i});//记录答案
                }
                int length = tmp.size();
                for(auto it = s.begin();*it !=length;it++){//这段代码是本题的精髓
                    int d = *it;
                    if(f(tmp,0,length-d-1) && m.count(tmp.substr(length-d))){
                        res.push_back({i,m[tmp.substr(length-d)]});
                    }
                    if(f(tmp,d,length-1) && m.count(tmp.substr(0,d))){
                        res.push_back({m[tmp.substr(0,d)],i});
                    }
                }
            }
            return res;
        }
    };
    复制代码

    思路2:
    从每个字符串的第一个字符遍历,依次增加长度。

    复制代码
    class Solution {
        public List<List<Integer>> palindromePairs(String[] words) {
            List<List<Integer>> ans = new ArrayList<List<Integer>>();
            Map<String,Integer> dict = new HashMap<>();
            for(int i = 0;i<words.length;i++){
                dict.put(words[i],i);//进哈希表
            }
            for(int j=0;j<words.length;j++){
                String str = words[j];
                int left = 0;
                int right = 0;
                while(left <= right){
                    String tmpstr = str.substring(left,right);//从每个字符串的第一个字符判断,有没有与之相对应的回文串
                    Integer k = dict.get(new StringBuilder(tmpstr).reverse().toString());
                    if(k != null && k != j && isPalindrome(str.substring(left == 0 ? right : 0,left ==0 ? str.length():left))){
                        ans.add(Arrays.asList(left == 0 ? new Integer[] {j,k}:new Integer[] {k,j}));
                    }
                    if(right < str.length()) right++;
                    else left++;
                }
            }
            return ans;
        }
        public boolean isPalindrome(String str){
            int start = 0,end = str.length()-1;
            while(start <= end){
                if(str.charAt(start) != str.charAt(end)) return false;
                start++;
                end--;
            }
            return true;
        }
    }
    复制代码

    解法1:
    递归回溯 ,跟全排列的算法一样

     

    复制代码
    class Solution {
    public:
        vector<vector<string>> result;
        vector<string> temp;
        bool isPalindrome(string s){
            int i=0,j=s.size()-1;
            while(i < j){
                if(s[i] != s[j]){
                    return false;
                }
                i++;
                j--;
            }
            return true;
        }
        void recursion(string s,int a,int b){
            //到字符串末尾了,将本次结果记录下来
            if(a > b){
                result.push_back(temp);
                return;
            }
            //从index为a开始截取长度为1,2,3...的子串进行验证,成功则用剩下的部分递归
            for(int i=1;i<=b-a+1;i++){
                if(isPalindrome(s.substr(a,i))){
                    temp.push_back(s.substr(a,i));
                    recursion(s,a+i,b);
                    temp.pop_back();
                }
            }
        }
        vector<vector<string>> partition(string s) {
            recursion(s,0,s.size()-1);
            return result;
        }
    };
    复制代码

     解法2:

    DP + 回溯

    思路:分两步。第一步对s使用dp方法确定回文子串。第二步使用回溯法进行分割。第一步,dp[i][j]代表s(i,j)是否为回文(包含i和j)字符串,注意当ij之间的字符大于等于3个时,dp[i][j]=dp[i+1][j-1]&&s[i]==s[j],即i递减,j递增,又j>=i,即可确定i,j的递推方向。第二步,dfs(i,j),将s(i,j)放入组合,对dp[j+1][k] (j+1<=k<n)中为true的进行dfs,当j==n-1时,当前组合放入结果。注意dfs的优化,模版类使用传址,复用变量(如字符串s的大小)使用全局变量。

    复制代码
    class Solution {
    public:
        int n;//字符串长度作为全局变量, 这样再dfs里不需声明变量
        vector<vector<string>> res;//结果
        vector<vector<string>> partition(string s) {
            n = s.size();//字符串长度
            vector<vector<bool>> dp(n,vector<bool>(n,false));//dp[i][j]表示s(i,j)是否为回文字符串
            for(int i=n-1;i>=0;i--){//i递减
                for(int j=i;j<n;j++){//j递增,且j>=i
                    if(i == j)//一个字母是回文
                        dp[i][j] = true;
                    else if(i == j-1){//相邻字母,相同是回文
                        if(s[i] == s[j]){
                            dp[i][j] = true;
                        }
                    }
                    else{//长度 >= 3, 中间为回文且首尾字母相同为回文
                        if(s[i] == s[j] && dp[i+1][j-1]){
                            dp[i][j] = true;
                        }
                    }
                }
            }
            vector<string> temp;//组合暂存变量 储存一个有效的回文串分割
            for(int i=0;i<n;i++){
                if(dp[0][i])
                    dfs(temp,dp,s,0,i);//从第一个字母开始,向后取回文串进行分割
            }
            return res;
        }
        void dfs(vector<string>& temp,vector<vector<bool>>& dp,string s,int i,int j){
            temp.push_back(s.substr(i,j-i+1));//s(i,j)放入组合
            if(j == n-1){
                res.push_back(temp);//到达单词结尾,把当前组合放入结果
            }
            j++;//从j+1开始继续找回文串
            for(int k=j;k<n;k++){
                if(dp[j][k])
                    dfs(temp,dp,s,j,k);
            }
            temp.pop_back();//尾回溯 退出时删除本轮添加的字符串
        }
    };
    复制代码

    解法1:DP+ 中心扩散法判读回文串

    复制代码
    class Solution {
        public int minCut(String s) {
            if(s == null || s.length() <= 1)
                return 0;
            int len = s.length();
            int dp[] = new int[len];
            Arrays.fill(dp,len-1);
            for(int i=0;i<len;i++){
                //注意偶数长度与奇数长度回文串的特点
                mincutHelper(s,i,i,dp);//奇数回文串以1个字符为中心
                mincutHelper(s,i,i+1,dp);//偶数回文串以两个字符为中心
            }
            return dp[len-1];
        }
        private void mincutHelper(String s,int i,int j,int[] dp){
            int len = s.length();
            while(i>=0 && j<len && s.charAt(i) == s.charAt(j)){
                dp[j] = Math.min(dp[j],(i==0 ? -1 :dp[i-1]) + 1);
                i--;
                j++;
            }
        }
    }
    复制代码

    C++版 中心扩散

    复制代码
    class Solution {
    public:
        int minCut(string s) {
            int n = s.size();
            vector<int> dp(n);
            for(int i=0;i<n;++i) dp[i] = i;
            for(int i=0;i<n;++i){
                centerExpand(s,dp,i,i);
                centerExpand(s,dp,i,i+1);
            }
            return dp[n-1];
        }
        void centerExpand(string& s,vector<int>& dp,int l,int r){
            while(l>=0 && r<s.size() && s[l] == s[r]){
                if(l==0) dp[r] = 0;
                else dp[r] = min(dp[r],dp[l-1]+1);
                --l;++r;
            }
        }
    };
    复制代码

     

     

    官方解法:https://leetcode-cn.com/problems/longest-valid-parentheses/solution/zui-chang-you-xiao-gua-hao-by-leetcode/

     解法1:

    思路:

    常规思路1:

    对于这种括号匹配问题,一般都是使用栈

    我们先找到所有可以匹配的索引号,然后找出最长连续数列!

    例如:s = )(()()) ,我们用栈可以找到,

    位置2和位置3匹配,

    位置4和位置5匹配,

    位置1和位置6匹配,

    这个数组为:2,3,4,5,1,6这是通过栈找到的,我们按递增排序!1,2,3,4,5,6

    找出该数组的最长连续数列的长度就是最长有效括号长度!

    所以时间复杂度来自排序:O(nlogn)

    接下来我们思考,是否可以在省略排序的过程,在弹栈时候进行操作!

    直接看代码理解!所以时间复杂度为:O(n)

    复制代码
    class Solution {
        public int longestValidParentheses(String s) {
            if(s == null || s.length() == 0) return 0;
            Deque<Integer> stack = new ArrayDeque<>();
            stack.push(-1);
            
            int res = 0;
            for(int i=0;i<s.length();i++){
                if(s.charAt(i) == '(') stack.push(i);
                else{
                    stack.pop();
                    if(stack.isEmpty()) stack.push(i);
                    else{
                        res = Math.max(res,i - stack.peek());
                    }
                }
            }
            return res;
        }
    }
    复制代码

    解法2:

    思路:

    **思路2:**

    dp方法:

    我们用dp[i]表示以i结尾的最长有效括号;

    1. s[i] (,dp[i] 必然等于0,因为不可能组成有效的括号;

    2. 那么s[i] 为)

      2.1 当s[i-1] 为(,那么dp[i] = dp[i-2] + 2;

      2.2 当s[i-1] 为)并且 s[i-dp[i-1] - 1](,那么dp[i] = dp[i-1] + 2 + dp[i-dp[i-1]-2];

    时间复杂度:$O(n)$

    复制代码
    class Solution {
        public int longestValidParentheses(String s) {
            if(s == null || s.length() == 0) return 0;
            int[] dp = new int[s.length()];
            int res = 0;
            for(int i=0;i<s.length();i++){
                if(i > 0 && s.charAt(i)== ')'){
                    if(s.charAt(i-1) == '('){
                        dp[i] = (i - 2 >=0 ? dp[i-2] + 2 : 2);
                    }else if(s.charAt(i - 1) == ')' && i - dp[i-1] - 1 >=0 && 
                            s.charAt(i - dp[i-1] -1) == '('){
                        dp[i] = dp[i-1] + 2 + (i - dp[i-1] -2 >=0 ? dp[i - dp[i-1]-2] : 0);
                    }
                }
                res = Math.max(res,dp[i]);
            }
            return res;
        }
    }
    复制代码

     


     解法1:

     DP 动态规划

    复制代码
    class Solution {
        public int numDistinct(String s, String t) {
            if(s == null || s.length() == 0){
                return 0;
            }
            int m = s.length();
            int n = t.length();
            int[][] dp = new int[m+1][n+1];
            //初始化dp[i][0] = 1;
            for(int i = 0;i<=m;i++){
                dp[i][0] = 1;
            }
            //按t[0:0] => t[0:n] 的顺序依次匹配
            for(int j = 1;j<=n;j++){
                //因为i < j时 s[0: j-1]的长度比t[0:j-1]小,所以可忽略
                for(int i=j;i<=m;i++){
                    if(s.charAt(i-1) == t.charAt(j-1)){
                        dp[i][j] = dp[i-1][j-1] + dp[i-1][j];
                    }else{
                        dp[i][j] = dp[i-1][j];
                    }
                }
            }
            return dp[m][n];
        }
    }
    复制代码
  • 相关阅读:
    返回一个整数数组中最大子数组的和 1
    软件工程第一周开课博客
    体温上报系统总结
    Android 布局中如何使控件居中
    Android:Unable to find explicit activity class报错
    Android Studio 线性布局
    相对布局
    HTML5 video视频
    阅读笔记——《人月神话》
    体温汇报系统界面
  • 原文地址:https://www.cnblogs.com/JasonPeng1/p/12118838.html
Copyright © 2020-2023  润新知