字符串题目汇总

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

给定一个非空的字符串,判断它是否可以由它的一个子串重复多次构成。给定的字符串只含有小写英文字母,并且长度不超过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)\n", 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;
    }
};

  

 

posted @ 2020-09-11 12:21  静悟生慧  阅读(672)  评论(0编辑  收藏  举报