zoukankan      html  css  js  c++  java
  • 字符串题目汇总

    难度:★☆☆☆☆
    类型:数组

    给定一个非空的字符串,判断它是否可以由它的一个子串重复多次构成。给定的字符串只含有小写英文字母,并且长度不超过10000。

    示例

    示例 1:
    输入: "abab"
    输出: True
    解释: 可由子字符串 "ab" 重复两次构成。

    示例 2:
    输入: "aba"
    输出: False

    示例 3:
    输入: "abcabcabcabc"
    输出: True
    解释: 可由子字符串 "abc" 重复四次构成。 (或者子字符串 "abcabc" 重复两次构成。)

     

    class Solution {
    public:
        bool repeatedSubstringPattern(string s) {
    
            // 常规解法,暴力思路,不同长度,依次进行比较判断
           
           
            // int len = s.length();
    
            // int m = len/2;
    
    
            // for(int i=1;i<=m;i++){
            //     int flag =1;
            //     if(len%i==0){
            //         string tmp= s.substr(0,i);
            //         int n= len/i;
            //         for(int k= 1;k<n;k++){
            //             if(tmp!=s.substr(k*i,i)){
            //                 flag =0;
            //                 break; 
            //             }
            //         }
    
            //         if (flag==1){
            //             return true;
            //         }
            //     }
            // }
    
            // return false;
    
            //https://leetcode-cn.com/problems/repeated-substring-pattern/solution/tu-jie-yi-xia-shuang-bei-zi-fu-chuan-de-jie-fa-by-/
            //双倍字符串的思路:图解一下双倍字符串的解法 - 重复的子字符串 - 力扣(LeetCode
    
            return (s+s).find(s,1)!=s.size();
    
        }
    };
    

      

    KMP算法,字符串匹配算法,O(m+n) https://www.zhihu.com/question/21923021/answer/1032665486
     

    请从字符串中找出一个最长的不包含重复字符的子字符串,计算该最长子字符串的长度。

    示例 1:

    输入: "abcabcbb"
    输出: 3
    解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
    示例 2:

    输入: "bbbbb"
    输出: 1
    解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。
    示例 3:

    输入: "pwwkew"
    输出: 3
    解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
      请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。
     

    class Solution {
    public:
        int lengthOfLongestSubstring(string s) {
    
            // int len = s.length();
    
            // if(len<=1){
            //     return len;
            // }
    
            // vector<int> res(len,1);
    
            // res[0]=1;
            // int start = 0;
            // int max = 1;
            // for(int i=1; i<len; i++){
            //     string tmp = s.substr(start,i-start);
            //     if(tmp.find(s[i])!=string::npos){
            //         res[i]=res[i-1];
            //         start = tmp.find(s[i])+1;
    
            //     }
            //     else{
            //         res[i]=i-start+1;
            //     }
    
            //     if(res[i]>max){
            //         max = res[i];
            //     }
            // }
    
            // return max;
    
            int len = s.length();
            int max = 1;
    
    
            if(len<=1){
                return len;
            }
    
            vector<int> res(len,1);
    
            int flag = 0;
            int start = 0;
            int i=0;
            int j=0;
    
    
            for (i=1;i<len; i++){
                for(j=i-1;j>=start;j--){
                    if(s[i]==s[j]){
                        flag = 1;
                        break;
                    }
                }
    
                if(!flag){
                    res[i] = res[i-1]+1;
                }
                else{
                    res[i]=i-j;
                    start = j;
                    flag = 0;
                }
    
                if(max < res[i]){
                    max=res[i];
                }
            }
    
    
            return max;
    
        }
    };
    

      

    动态规划思路:https://blog.csdn.net/qq_27690765/article/details/105439787

    dp[i] 表示以第 i 个字符结尾的最长子字符串的长度。

    分两种情况:

    1)如果str[i] 和前面的所有字符都不一样,则dp[i]=dp[i-1] +1

    2)如果str[i] 和前面的某个字符都一样,则以str[i] 结尾的最大不重复的字串 dp[i]=i-j  (0<j<i)

    滑动窗口思路,解题框架见下面字符串题目:

    class Solution {
    public:
        int lengthOfLongestSubstring(string s) {
            unordered_map<char, int> window;
            int left = 0, right = 0;
            int res = 0; 
            // 记录结果 
    
            while (right < s.size()) {
                char c = s[right]; 
                right++;
                // 进行窗口内数据的一系列更新 
                window[c]++;
                // 判断左侧窗口是否要收缩 
    
                while (window[c] > 1) {
                    char d = s[left];
                    left++;
                    window[d]--; 
                }
                // 在这里更新答案
                res = max(res, right - left); 
            }
            return res; 
        }
    };
    

      

    在字符串 s 中找出第一个只出现一次的字符。如果没有,返回一个单空格。 s 只包含小写字母。

    示例:

    s = "abaccdeff"
    返回 "b"

    s = ""
    返回 " "
     

    限制:

    0 <= s 的长度 <= 50000

    class Solution {
    public:
        char firstUniqChar(string s) {
            int len = s.length();
            if(len==0){
                return ' ';
            }
    
            int hash_char[256] = {0};
    
            for(int i=0;i<len;i++){
                hash_char[s[i]]++;
            }
    
            for(int j=0;j<len;j++){
                if(hash_char[s[j]]==1){
                    return s[j];
                }
            }
            return ' ';
        }
    };
    

      

    写一个函数 StrToInt,实现把字符串转换成整数这个功能。不能使用 atoi 或者其他类似的库函数。

    首先,该函数会根据需要丢弃无用的开头空格字符,直到寻找到第一个非空格的字符为止。

    当我们寻找到的第一个非空字符为正或者负号时,则将该符号与之后面尽可能多的连续数字组合起来,作为该整数的正负号;假如第一个非空字符是数字,则直接将其与之后连续的数字字符组合起来,形成整数。

    该字符串除了有效的整数部分之后也可能会存在多余的字符,这些字符可以被忽略,它们对于函数不应该造成影响。

    注意:假如该字符串中的第一个非空格字符不是一个有效整数字符、字符串为空或字符串仅包含空白字符时,则你的函数不需要进行转换。

    在任何情况下,若函数不能进行有效的转换时,请返回 0。

    说明:

    假设我们的环境只能存储 32 位大小的有符号整数,那么其数值范围为 [−231,  231 − 1]。如果数值超过这个范围,请返回  INT_MAX (231 − 1) 或 INT_MIN (−231) 。

    示例 1:

    输入: "42"
    输出: 42
    示例 2:

    输入: " -42"
    输出: -42
    解释: 第一个非空白字符为 '-', 它是一个负号。
      我们尽可能将负号与后面所有连续出现的数字组合起来,最后得到 -42 。
    示例 3:

    输入: "4193 with words"
    输出: 4193
    解释: 转换截止于数字 '3' ,因为它的下一个字符不为数字。
    示例 4:

    输入: "words and 987"
    输出: 0
    解释: 第一个非空字符是 'w', 但它不是数字或正、负号。
    因此无法执行有效的转换。
    示例 5:

    输入: "-91283472332"
    输出: -2147483648
    解释: 数字 "-91283472332" 超过 32 位有符号整数范围。
      因此返回 INT_MIN (−231)

    class Solution {
    public:
        int strToInt(string str) {
            int len = str.length();
            if(len==0){
              return 0;
            }
    
            int i=0;
    		//跳过前面空格,判断正负
            while(str[i]==' '){
                i++;
            }
            
            bool neg = false;
            //对非空和非+-号的有效数字进行处理
            if(str[i]=='-'){
                neg=true;
                i++;
            }else if(str[i]=='+'){
                i++;
            }
    
            long ans =0;
            for(;i<len;i++){
                if(str[i]>='0'&&str[i]<='9'){
                //***字符相减转换成int
                    ans = ans*10+(str[i]-'0');
                    if(ans > INT_MAX && neg) return INT_MIN;
                    if(ans > INT_MAX && !neg) return INT_MAX;
            	}
            	//碰到非有效数字就退出
                else{
                    break;
                }
            }
            return neg?-ans:ans;
    
        }
    };
    

      

    • 最长回文子串:找到给出字符串可能构成的最长回文长度

      思路:中心扩展法 、动态规划法

    给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。

    示例 1:

    输入: "babad"
    输出: "bab"
    注意: "aba" 也是一个有效答案。

    示例 2:

    输入: "cbbd"
    输出: "bb"


    解法一:动态规划

    从回文子串的定义我们可以得出两个结论:

    (1)如果说一个子字符串是回文子串,那么给这个子字符串两边分别加一个数,如果这2个数相等,那么这个新的子字符串也是回文子串。

    (2)回文子串的开始和结束位置的字符一定是相等的。

    我们可以用i来表示一个子字符串的起始位置,j来表示一个子字符串的结束位置,用一个二维数组来存储i和j之间的子字符串是否是回文子串。

    状态转移方程:
    dp[i][j] :
    i=j时为true ;
    i-j=1时若s[i]==s[j]则为true
    i-j>1时若s[i]==s[j]&&dp[i-1][j-1]都为真,则为true;

    class Solution {
        public:
        string longestPalindrome(string s) {
            //动态规划做
            int l=s.size();
            if(l==0)
                return "";
            int max_l=0,max_r=0;
            vector<vector<int>>dp(l,vector<int>(l,0));
            for(int i=0;i<l;++i)
                dp[i][i]=1;
            for(int right=1;right<l;++right)
            {
                for(int left=0;left<right;++left)
                {
                    if(s[left]==s[right]&&(right-left==1||dp[left+1][right-1]))
                    {
                        dp[left][right]=1;
                        if(right-left>max_r-max_l)
                        {
                            max_r=right;
                            max_l=left;
                        }
                    }
                }
            }
            return s.substr(max_l,max_r-max_l+1);    
        }
    };
    

      

    解法2:中心扩展法,以每一个字符或者两个字符分别作为回文串中心,向两端扩展,判断是否依旧是回文串

    class Solution {
    public:
        string longestPalindrome(string s) {
            int n = s.size(), rmax = 0;
            string res;
    
            // 中心扩展法(对称)
            for(int k = 0; k < n; k ++) {
                int i = k - 1, j = k + 1;
                while(i >= 0 && j < n && s[i] == s[j]) i --, j ++;
                int len = j - i - 1;
                if (len > rmax) {
                    rmax = len;
                    res = s.substr(i + 1, len);
                }
            }
    
            for (int k = 0; k < n - 1; k ++) {
                int i = k, j = k + 1;
                while(i >= 0 && j < n && s[i] == s[j]) i --, j ++;
                int len = j - i - 1;
                if (len > rmax) {
                    rmax = len;
                    res = s.substr(i + 1, len);
                }
            }
            return res;
        }
    };
    

      

    回文子串 

    给定一个字符串,你的任务是计算这个字符串中有多少个回文子串。

    具有不同开始位置或结束位置的子串,即使是由相同的字符组成,也会被视作不同的子串。

    示例 1:

    输入:"abc"
    输出:3
    解释:三个回文子串: "a", "b", "c"
    示例 2:

    输入:"aaa"
    输出:6
    解释:6个回文子串: "a", "a", "a", "aa", "aa", "aaa"
     

    提示:

    输入的字符串长度不会超过 1000 。

    中心扩展法:

    class Solution {
    public:
        int countSubstrings(string s) {
            int ans = 0;
            int len = s.length();
            for(int i=0;i<2*len-1;i++){
                int left = i/2;
                int right = left + i%2;
                while(left>=0&&right<len&&s[left]==s[right]){
                    ans++;
                    left--;
                    right++;
                }
            }
            return ans;
    
        }
    };
    

      


    字符串有三种编辑操作:插入一个字符、删除一个字符或者替换一个字符。 给定两个字符串,编写一个函数判定它们是否只需要一次(或者零次)编辑。

    示例 1:

    输入:
    first = "pale"
    second = "ple"
    输出: True
     

    示例 2:

    输入:
    first = "pales"
    second = "pal"
    输出: False

    思路1,采用编辑距离:编辑距离大于1则返回false,否则返回true;


    思路2,双指针的思路
    特殊情况判断,长度差大于1的直接返回false
    然后遍历比较两个字符串,first[i] == second[j]) 则继续
    如果:first[i] == second[j+1] ,则 j++编辑次数 op_cnt++
    first[i+1] == second[j,则i++编辑次数 op_cnt++

    否则:i++,j++ op_cnt++
    如果,op_cnt>1 返回false

    最终遍历完成,如果剩下的字符个数和op_cnt相加大于1也返回false

    class Solution {
    public:
      bool oneEditAway(string first, string second) {
        int len1 = first.size(), len2 = second.size();
        if (abs(len1 - len2) > 1) return false;
        int i = 0, j = 0;
        int op_cnt = 0;
        while (i < len1 && j < len2) {
          if (first[i] == second[j]) {
            i++, j++;
          } else {
            if (first[i] == second[j+1]) {
              j++;
              if (op_cnt > 0) return false;
              else op_cnt++;
            } else if (first[i+1] == second[j]) {
              i++;
              if (op_cnt > 0) return false;
              else op_cnt++;
            } else {
              i++, j++;
              if (op_cnt > 0) return false;
              else op_cnt++;
            }
          }
        }
        if (max(len1 - i, len2 - j) + op_cnt > 1) return false;
        return true;
      }
    };
    

      

    有效的括号


    给定一个只包括 '(',')','{','}','[',']' 的字符串,判断字符串是否有效。

    有效字符串需满足:

    左括号必须用相同类型的右括号闭合。
    左括号必须以正确的顺序闭合。
    注意空字符串可被认为是有效字符串。

    示例 1:

    输入: "()"
    输出: true
    示例 2:

    输入: "()[]{}"
    输出: true
    示例 3:

    输入: "(]"
    输出: false
    示例 4:

    输入: "([)]"
    输出: false
    示例 5:

    输入: "{[]}"
    输出: true

    思路:用栈来实现,遇到左括号入栈,右括号出栈;

    class Solution {
    public:
        bool isValid(string s) {
        	int len = s.size();
    
        	if(len%2!=0){
        		return false;
        	}
    
        	stack<char> st;
        	st.push(s[0]);
    
        	for(int i=1;i<len;i++){
        		if(s[i]=='('||s[i]=='{'||s[i]=='['){
        			st.push(s[i]);
        		}else if(!st.empty()){
        			if(s[i]==']'){
        				if(st.top()=='['){
        					st.pop();
        				}else{
        				return false;
        				}
        			}
        			if(s[i]==')'){
        				if(st.top()=='('){
        					st.pop();
        				}else{
        				return false;
        			}
        			}
    
        			if(s[i]=='}'){
        				if(st.top()=='{'){
        					st.pop();
        				}else{
        				return false;
        			}
        			}
    
        		}
                else if(st.empty()){
                    return false;
                }
        	}
    
        	if(st.empty()){
        		return true;
        	}
    
        	return false;
    
        }
    };
    

      

    最小覆盖子串:

    给你一个字符串 S、一个字符串 T 。请你设计一种算法,可以在 O(n) 的时间复杂度内
    ,从字符串 S 里面找出:包含 T 所有字符的最小子串。

    示例:

    输入:S = "ADOBECODEBANC", T = "ABC"
    输出:"BANC"
     

    提示:

    如果 S 中不存这样的子串,则返回空字符串 ""。
    如果 S 中存在这样的子串,我们保证它是唯一的答案。

    暴力思路:

    for (int i = 0; i < s.size(); i++)
        for (int j = i + 1; j < s.size(); j++)
            if s[i:j] 包含 t 的所有字母: 
                更新答案
    

     

    解题思路:滑动窗口

    /* 滑动窗口算法框架 */
    void slidingWindow(string s, string t) {
        unordered_map<char, int> need, window;
        for (char c : t)
            need[c]++;
        int left = 0, right = 0;
        int valid = 0;
        while (right < s.size()) {
            // c 是将移入窗口的字符 
            char c = s[right]; 
            // 右移窗口
            right++;
            // 进行窗口内数据的一系列更新 ...
            /*** debug 输出的位置 ***/
            printf("window: [%d, %d)
    ", left, right); 
            /********************/
    
            // 判断左侧窗口是否要收缩
            while (window needs shrink) {
                // d 是将移出窗口的字符
                char d = s[left];
                // 左移窗口
                left++;
                // 进行窗口内数据的一系列更新 
                ...
            } 
        }
    }
    

      

    class Solution {
    public:
        string minWindow(string s, string t) {
            unordered_map<char, int> need, window;
            for (char c : t) need[c]++;
            int left = 0, right = 0;
            int valid = 0;
    
            // 记录最小覆盖子串的起始索引及⻓度 
            int start = 0, len = INT_MAX; 
    
            while (right < s.size()) {
                // c 是将移入窗口的字符 
                char c = s[right]; 
                // 右移窗口
                right++;
                // 进行窗口内数据的一系列更新 
                if (need.count(c)) {
                    window[c]++;
                if (window[c] == need[c])
                    //满足的字符的数量
                    valid++;
                }
            
                // 判断左侧窗口是否要收缩
                while (valid == need.size()) {
                    // 在这里更新最小覆盖子串
                    if (right - left < len) {
                        start = left;
                        len = right - left; 
                    }
                    // d 是将移出窗口的字符 
                    char d = s[left];
                    // 左移窗口
                    left++;
                    // 进行窗口内数据的一系列更新 
                    if (need.count(d)) {
                        if (window[d] == need[d]) 
                            valid--;
                        window[d]--; 
                    }
                }
            }
    
            return len == INT_MAX ? "" : s.substr(start, len);
    
        }
    };
    

      

    给定两个字符串 s1 和 s2,写一个函数来判断 s2 是否包含 s1 的排列。

    换句话说,第一个字符串的排列之一是第二个字符串的子串。

    示例1:

    输入: s1 = "ab" s2 = "eidbaooo"
    输出: True
    解释: s2 包含 s1 的排列之一 ("ba").
    

    示例2:

    输入: s1= "ab" s2 = "eidboaoo"
    输出: False
    

     注意:

    1. 输入的字符串只包含小写字母
    2. 两个字符串的长度都在 [1, 10,000] 之间

      

    思路:同样滑动窗口

    注意,输入的 s1 是可以包含重复字符的,所以这个题难度不小。
    这种题目,是明显的滑动窗口算法,相当给你一个 S 和一个 T ,请问你 S 中是否存在一个子串,
    包含 T 中所有字符且不包含其他字符?

    class Solution {
    public:
        bool checkInclusion(string t, string s) {
        unordered_map<char, int> need, window;
        for (char c : t) need[c]++;
        int left = 0, right = 0; 
        int valid = 0;
        while (right < s.size()) {
            char c = s[right]; 
            right++;
            // 进行窗口内数据的一系列更新 
            if (need.count(c)) {
                window[c]++;
            if (window[c] == need[c])
                valid++;
            }
    
            // 判断左侧窗口是否要收缩,这里判断条件是是否大于目标串的长度
            while (right - left >= t.size()) {
                // 在这里判断是否找到了合法的子串
                if (valid == need.size())
                    return true;
    
                char d = s[left];
                left++;
                // 进行窗口内数据的一系列更新 
                if (need.count(d)) {
                    if (window[d] == need[d])
                        valid--;
                    window[d]--; 
                }
            } 
        }
        // 未找到符合条件的子串
        return false;
        }
    };
    

      

    对于这道题的解法代码,基本上和最小覆盖子串一模一样,只需要改变两个 地方:
    1、本题移动 left 缩小窗口的时机是窗口大小大于 t.size() 时,应为排列嘛,显然⻓度应该是一样的。
    2、当发现 valid == need.size() 时,就说明窗口中就是一个合法的排列, 所以立即返回 true 。
    至于如何处理窗口的扩大和缩小,和最小覆盖子串完全相同。

    给定一个字符串 s 和一个非空字符串 p,找到 s 中所有是 p 的字母异位词的子串,返回这些子串的起始索引。

    字符串只包含小写英文字母,并且字符串 s 和 p 的长度都不超过 20100。

    说明:

    • 字母异位词指字母相同,但排列不同的字符串。
    • 不考虑答案输出的顺序。

    示例 1:

    输入:
    s: "cbaebabacd" p: "abc"
    
    输出:
    [0, 6]
    
    解释:
    起始索引等于 0 的子串是 "cba", 它是 "abc" 的字母异位词。
    起始索引等于 6 的子串是 "bac", 它是 "abc" 的字母异位词。
    

     示例 2:

    输入:
    s: "abab" p: "ab"
    
    输出:
    [0, 1, 2]
    
    解释:
    起始索引等于 0 的子串是 "ab", 它是 "ab" 的字母异位词。
    起始索引等于 1 的子串是 "ba", 它是 "ab" 的字母异位词。
    起始索引等于 2 的子串是 "ab", 它是 "ab" 的字母异位词。

    思路:跟寻找字符串的排列一样,只是找到一个合法异位词(排列)之后将起始索 引加入 即可。

    class Solution {
    public:
        vector<int> findAnagrams(string s, string p) {
            unordered_map<char, int> need, window;
            vector<int> res;
            for (char c : p) need[c]++;
            int left = 0, right = 0; 
            int valid = 0;
            while (right < s.size()) {
                char c = s[right]; 
                right++;
                // 进行窗口内数据的一系列更新 
                if (need.count(c)) {
                    window[c]++;
                if (window[c] == need[c])
                    valid++;
                }
    
                // 判断左侧窗口是否要收缩,这里判断条件是是否大于目标串的长度
                while (right - left >= p.size()) {
                    // 在这里判断是否找到了合法的子串
                    if (valid == need.size())
                        res.push_back(left);
    
                    char d = s[left];
                    left++;
                    // 进行窗口内数据的一系列更新 
                    if (need.count(d)) {
                        if (window[d] == need[d])
                            valid--;
                        window[d]--; 
                    }
                } 
            }
            // 未找到符合条件的子串
            return res;
    
        }
    };
    

      


    输入两个值n和k,n表示我们有从1到n个整数,然后将这些整数都字符串化之后按字典排序,找出其中第K大的。
    例如:n=15,k=5.那么1-15字符串化之后排序如下:1,10,11,12,13,14,15,2,3,4,5,6,7,8,9。其中第5大的就为13。
    求解: 限定n<100,求解一百以内的排序。
    思路:
    1,10,11,12,13,14,15,16,17,18,19,2,20,21,22,23,…
    将数列分层,即1–>19为一层,一共为11个元素,记作:l=(x-10)/10; 对于一层内,存在如下关系(不包括元素0):(l+1)*10为高位,余数为低位。

    #include <iostream>
    using namespace std;
     
    int main(){
        int n=37,k;
        int l;
        for(k=1;k<=n;k++){
    //      cout<<" k "<<k;
            if(k==1){
                cout<<1<<" ";
                continue;
            }
            if(n<10){
                cout<<k<<endl;
            }
            else{
                l=(n-10)/10;
                if(k<=11*l){
                    int a=(k-1)/11;
                    int b=k-a*11;
                    if(b==1){
        //              cout<<" a "<<a+1<<endl;
                        cout<<a+1<<" ";
                        continue;
                    }
        //          cout<<" b "<<(a+1)*10+b-2<<endl;
                    cout<<(a+1)*10+b-2<<" ";
                }
                else{
                    int b=n-10-10*l;
                    int a1=k/11;
                    int b1=k-l*11;
                    if(b1==1){
                        cout<<a1+1<<" ";
        //              cout<<" e "<<a1+1<<endl;
                        continue;
                    } 
                    if(b1<=b+2){
                        cout<<(a1+1)*10+b1-2<<" ";
        //              cout<<" c "<<(a1+1)*10+b1-2<<endl;
                    }
                    else{
        //              cout<<" d "<<b1-b-1+l<<endl;
                        cout<<b1-b-1+l<<" ";
                    }
                }
                
            }
        }
    }
    

      

    // 另一种写法
    #include <iostream>
    #include <cstdlib>
    
    using namespace std;
    int main(){
        int n,k;
        cin>>n>>k;
        if(k == 1) cout<<1<<endl;
        else{
            k--;
            int base = 10;
            int res;
            while(k > 0){
                res = base;
                while(res<n && k>0){
                    res*=10;
                    k--;
                }
                res=res/10;
                while(res<n && k>0){
                    res++;
                    k--;
                }
                base++;
            }
            cout<<res<<endl;
        }
    
        return 0;
    }
    

      

    387. 字符串中的第一个唯一字符
    给定一个字符串,找到它的第一个不重复的字符,并返回它的索引。如果不存在,则返回 -1。

    示例:

    s = "leetcode"
    返回 0

    s = "loveleetcode"
    返回 2

    提示:你可以假定该字符串只包含小写字母。

    思路:hash表的思想,利用ASCII码表,定义一个长度为256的数组,遍历字符串,统计字符出现的次数;
    最后重新遍历字符串,找到一个计数为1的字符即可;

    #include <iostream>  //利用ASCII码表;
    using namespace std;
     
    class Solution {
    public:
        int firstUniqChar(string s)
        {
            int p[256] = { 0 }; 
            int n = s.size();
    
            for (int i = 0; i < n; i++){
                p[s[i]] += 1;
            }
            for (int j = 0; j < n; j++){
                if (p[s[j]] == 1){
                    return j;
                }
            }
            return -1;
        }
    };
    
    
    int main()
    {
        Solution temp;
        string s = "loveleetcode";
        cout << temp.firstUniqChar(s) << endl;
        system("pause");
        return 0;
    }
    

      

    14. 最长公共前缀


    编写一个函数来查找字符串数组中的最长公共前缀。

    如果不存在公共前缀,返回空字符串 ""。

    示例 1:

    输入: ["flower","flow","flight"]
    输出: "fl"
    示例 2:

    输入: ["dog","racecar","car"]
    输出: ""
    解释: 输入不存在公共前缀。

    思路:依次遍历字符串数组中的每个字符串,对于每个遍历到的字符串,更新最长公共前缀,当遍历完所有的字符串以后,即可得到字符串数组中的最长公共前缀。

    class Solution {
    public:
        string longestCommonPrefix(vector<string>& strs) {
            if (!strs.size()) {
                return "";
            }
            string prefix = strs[0];
            int count = strs.size();
            // 遍历整个数组,得到所有数组的结果
            for (int i = 1; i < count; ++i) {
                prefix = longestCommonPrefix(prefix, strs[i]);
                if (!prefix.size()) {
                    break;
                }
            }
            return prefix;
        }
    
        //先求两个字符串的最长公共前缀
        string longestCommonPrefix(const string& str1, const string& str2) {
            int length = min(str1.size(), str2.size());
            int index = 0;
            while (index < length && str1[index] == str2[index]) {
                ++index;
            }
            return str1.substr(0, index);
        }
    };
    

      

    6. Z 字形变换
    将一个给定字符串根据给定的行数,以从上往下、从左到右进行 Z 字形排列。

    比如输入字符串为 "LEETCODEISHIRING" 行数为 3 时,排列如下:

    L C I R
    E T O E S I I G
    E D H N
    之后,你的输出需要从左往右逐行读取,产生出一个新的字符串,比如:"LCIRETOESIIGEDHN"。


    思路:找到字符串的排列规律,具体规律参见:https://zhuanlan.zhihu.com/p/103001655

    class Solution {
    public:
    string convert(string s, int numRows) {
        if (numRows == 1) return s;//只有一行,直接返回
        int l=s.size();
        if(l<=numRows)//如果长度小于行数,也是直接返回的
            return s;
        int step = numRows * 2 - 2; // 间距
        int index ;// 记录s的下标,表明下一个需要放入的元素。
        int juli = 0; // 这是每一步的步长。
        string res;
        for (int i = 0; i < numRows; i++) // i表示行号
        {
            index = i;
            juli = i * 2;
            while (index < l)//超出字符串长度计算下一层
            {
                res += s[index]; // 当前行的第一个字母
                juli = step - juli;// 第一次步长是step -2*i,第二次是2*i,相当于a=b-c,c=b-a,这样不断循环。 
                index += (i == 0 || i == numRows-1) ? step : juli; // 0行和最后一行使用step间距,其余使用add间距
            }
        }
        return res;
    }
    };
    

      

    763. 划分字母区间
    字符串 S 由小写字母组成。我们要把这个字符串划分为尽可能多的片段,同一字母最多出现在一个片段中。返回一个表示每个字符串片段的长度的列表。

    示例:

    输入:S = "ababcbacadefegdehijhklij"
    输出:[9,7,8]
    解释:
    划分结果为 "ababcbaca", "defegde", "hijhklij"。
    每个字母最多出现在一个片段中。
    像 "ababcbacadefegde", "hijhklij" 的划分是错误的,因为划分的片段数较少。


    思路:
    用vector容器ends来存放26个字母中各个字母在字符串中出现的最后位置。
    j从0遍历至字符串结束,先确定pos为j所在字符的最后一个位置,即ends[S[j]-'a'],
    在j+1到pos之间,用k遍历,判断每个字符字母的最后位置,比较是否大于pos,以此来更新pos,直到k==pos,说明一个小片段已经形成,加入答案中,令j=pos+1继续。

    参考:https://blog.csdn.net/Yirschen/article/details/105353518

    class Solution {
    public:
        vector<int> partitionLabels(string S) {
            vector<int> res;
            //ends存放字符串中各个字符(共26个,每一个按下标0--26表示)的最后位置
            vector<int> ends(26,-1);
            //记录每个字符最后出现的位置
            for(int i=0;i<S.size();i++){
                ends[S[i]-'a']=i;
            }
    
            int j=0;
            while(j<S.size()){
                //pos为第j个字符的字母在字符串中的最后位置
                int pos=ends[S[j]-'a'];
                //判断在第j+1~pos个字符串中间是否存在某个字符在pos后面,若有,更新pos
                for(int k=j+1;k<=pos;++k){
                    pos=max(pos,ends[S[k]-'a']);
                }
                //到这个位置,说明一个小片段已经形成,加入答案列表中,j更新为pos+1
                res.push_back(pos-j+1);
                j=pos+1;
            }
    
    
            return res;
    
        }
    };
    

      

    #include <iostream>
    #include <algorithm>
    #include <vector>
    #include <string>   
    using namespace std;
    
    vector<int> partitionLabels(string S) {
        vector<int> ans,ends(26, -1);//ends存放字符串中各个字符(共26个,每一个按下标0--26表示)的最后位置
        for (int i = 0; i < S.length(); ++i) {//更新每个字母的最后位置
            ends[S[i] - 'a'] = i;
        }
        int i = 0;
        while (i < S.size()) {
            int r = ends[S[i] - 'a'];//r为第i个字符的字母在字符串中的最后位置
            //判断在第i+1--r个字符串中间是否存在某个字符在r后面,若有,更新r
            for (int j = i + 1; j <= r; ++j) {
                r = max(r, ends[S[j] - 'a']);
            }
            //到这个位置,说明一个小片段已经形成,加入答案列表中,i更新为r+1
            ans.push_back(r - i + 1);
            i = r + 1;
        }
        return ans;
    }
    int main() {
        string s;
        cin >> s;
        vector<int> a = partitionLabels(s);
        for (vector<int>::iterator it = a.begin(); it != a.end(); ++it) {
            cout << *it << " ";
        }
        return 0;
    }
    

      


    1143. 最长公共子序列
    给定两个字符串 text1 和 text2,返回这两个字符串的最长公共子序列的长度。

    一个字符串的 子序列 是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串。
    例如,"ace" 是 "abcde" 的子序列,但 "aec" 不是 "abcde" 的子序列。两个字符串的「公共子序列」是这两个字符串所共同拥有的子序列。

    若这两个字符串没有公共子序列,则返回 0。

    示例 1:

    输入:text1 = "abcde", text2 = "ace"
    输出:3
    解释:最长公共子序列是 "ace",它的长度为 3。
    示例 2:

    输入:text1 = "abc", text2 = "abc"
    输出:3
    解释:最长公共子序列是 "abc",它的长度为 3。
    示例 3:

    输入:text1 = "abc", text2 = "def"
    输出:0
    解释:两个字符串没有公共子序列,返回 0。


    思路:动态规划思路,字符串str1前i个字符和str2前j个字符的最长公共子串长度,dp[i][j]
    str1[i]==str2[j]时,dp[i][j]=dp[i-1][j-1]+1;
    str1[i]!=str2[j]时,dp[i][j]=max(dp[i][j-1],dp[i-1][j]);
    当i=0或者j=0时,dp[i][j]=0;

    class Solution {
    public:
        int longestCommonSubsequence(string text1, string text2) {
            int len1=text1.size();
            int len2=text2.size();
    
            if(len1==0||len2==0){
                return 0;
            }
    
            vector<vector<int>> dp(len1+1,vector<int>(len2+1));
    
            for(int i=1;i<len1+1;i++){
                for(int j=1;j<len2+1;j++){
                    if(text1[i-1]==text2[j-1]){
                        dp[i][j]=dp[i-1][j-1]+1;
                    }else{
                        dp[i][j]=max(dp[i][j-1],dp[i-1][j]);
                    }
                }
            }
            return dp[len1][len2];
        }
    };
    

      


    Leetcode 394. 字符串编码
    给定一个经过编码的字符串,返回它解码后的字符串。

    编码规则为: k[encoded_string],表示其中方括号内部的 encoded_string 正好重复 k 次。注意 k 保证为正整数。

    你可以认为输入字符串总是有效的;输入字符串中没有额外的空格,且输入的方括号总是符合格式要求的。

    此外,你可以认为原始数据不包含数字,所有的数字只表示重复的次数 k ,例如不会出现像 3a 或 2[4] 的输入。

    示例:

    s = "3[a]2[bc]", 返回 "aaabcbc".

    s = "3[a2[c]]", 返回 "accaccacc".

    s = "2[abc]3[cd]ef", 返回 "abcabccdcdcdef".


    求解
    本题中明显有括号的匹配问题,因此需要使用栈来求解。当碰到右括号(])时,字符串出栈,碰到左括号([)时,
    保存左右括号内的字符串([]),继续出栈,保存字符串重复次数,直至栈为空或碰到非数字。要注意重复次数不是个位数,
    将字符串重复之后压入栈中。继续处理剩余字符串,同样执行上述过程,直至处理完字符串。然后将栈中所有的字符出栈构成结果字符串返回。

    设计两个栈,一个用来保存字符串,一个用来保存出现次数;
    1)如果遇到的是数字,直到遇到左括号前,求出整数,然后入栈数字,入栈当前字符串,然后置为空
    2)当遇到右括号时,然后看数字栈中有多少重复次数,出栈栈顶字符串,重复对应次数
    3)其它字符不处理,直接拼接上去

    思考:其实写代码的时候,可以多思考,尝试一下,把想到的表达出来,或者写出来

    class Solution {
    public:
        string decodeString(string s) {
            string cur ="";
            stack<string> st;
            stack<int> num;
            int sum;
    
            for(int i=0;i<s.size();i++){
                if(s[i]>='0'&&s[i]<='9'){
                    sum=0;
                    while(s[i]!='['){
                        sum = sum*10 + s[i++] - '0';
                    }
                    num.push(sum);
                    st.push(cur);
                    cur = "";
                }
                else if(s[i]==']'){
                    string temp=st.top();
                    for(int j=0;j<num.top();j++){
                        temp = temp + cur;
                    }
                    cur = temp;
                    st.pop();
                    num.pop();
                }
                else{
                    cur = cur+s[i];
                }
            }
            return cur;
        }
    };
    

      

    class Solution {
    public:
        string decodeString(string s) {
            stack<int> numStack;
            stack<string> strStack;
            string cur = "";
            string result = ""; //保存展开后的字符串
            int num = 0;
            for(int i=0;i<s.length();i++)
            {
                if(s[i]>='0'&&s[i]<='9')
                {
                    num = 10*num + s[i] - '0';
                }
                else if(s[i] == '[')
                {
                    numStack.push(num);
                    strStack.push(cur);
                    num=0;
                    cur.clear();
                }
                else if(s[i]>='a' && s[i]<='z' || s[i]>='A' && s[i]<='Z')
                {
                    cur += s[i];
                }
                else if(s[i] == ']')
                {
                    int k = numStack.top();
                    numStack.pop();
                    for(int j=0;j<k;j++) //重复的过程
                    {
                        strStack.top()+=cur;
                    }
                    cur = strStack.top();
                    strStack.pop();
                }
            }
            result += cur;
            return result;
        }
    };
    

      

  • 相关阅读:
    通过WebService跨平台上传大文件到服务器
    C# 委托、Lambda表达式和事件——学习总结
    WIN8、WIN7访问Windows Server 2003服务器的数据库速度很慢、远程速度很慢的解决方法
    C#中接口与抽象类的区别
    DataGridView重查后,返回原来所在行
    需要开拓的领域
    FTP使用心得
    VS的几个实用快捷键
    C#中窗体、MDI的使用心得
    水晶报表使用心得
  • 原文地址:https://www.cnblogs.com/Allen-rg/p/13650991.html
Copyright © 2011-2022 走看看