【字符串】知识点及相关题目详解

  • 熟悉字符串中的 基本操作,尤其是在数组中没有的独特操作;
  • 理解不同 比较 函数之间的区别;
  • 理解字符串 是否可变以及导致连接过程中出现的问题;
  • 能够解决与字符串相关的基本问题,如排序、子串、字符串匹配等。

KMP算法

				核心思想  减少模式串主串的匹配次数(主指针不回溯)。
				实现关键  模式串 构造next数组。
				复杂度    O(m+n)

关键 : 构造 next 数组

在这里插入图片描述

// s[]是长文本,p[]是模式串,n是s的长度,m是p的长度
#include <string>
#include <iostream>
using namespace std;
cnst int N = 1000010;
string p, s;
int ne[N];

int main()
{
    cin >> s >> p;
    ne[0] = 0;
    ne[1] = 0;
    int i = 2, j = 0;
    //求模式串的next数组-----------------------------------------------
    for (i = 2; i < p.size() + 1; i++)
    {
        while (j && p[i - 1] != p[j]) j = ne[j];
        if (p[i - 1] == p[j]) j++;
        ne[i] = j;
    }
    //匹配-------------------------------------------------------------
    for (i = 0, j = 0; i < s.size(); i++)
    {
        while (j && s[i] != p[j]) j = ne[j];
        if (s[i] == p[j]) j++;
        if (j == p.size())
        {
            cout << i - j + 1 << " ";
            return 0;
        }
    }
}

最后总结下这个算法:

  1. 匹配失败时,总是能够让模式串回退到某个位置,使文本不用回退。
  2. 在字符串比较时,模式串提供的信息越多,计算复杂度越低。(有兴趣的可以了解一下 Trie 树,这是文本提供的信息越多,计算复杂度越低的一个例子。)

PMT数组 中的值是字符串的前缀集合与后缀集合的交集中最长元素的长度。例如,对于”aba”,它的前缀集合为{”a”, ”ab”},后缀 集合为{”ba”, ”a”}。两个集合的交集为{”a”},那么长度最长的元素就是字符串”a”了,长 度为1,所以对于”aba”而言,它在PMT表中对应的值就是1。再比如,对于字符串”ababa”,它的前缀集合为{”a”, ”ab”, ”aba”, ”abab”},它的后缀集合为{”baba”, ”aba”, ”ba”, ”a”}, 两个集合的交集为{”a”, ”aba”},其中最长的元素为”aba”,长度为3。
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
则图中最长公共前缀后缀长度为0;
下面再以”ABCDABD”为例,进行介绍:
- ”A”的前缀和后缀都为空集,共有元素的长度为0;
- ”AB”的前缀为[A],后缀为[B],共有元素的长度为0;
- ”ABC”的前缀为[A, AB],后缀为[BC, C],共有元素的长度0;
- ”ABCD”的前缀为[A, AB, ABC],后缀为[BCD, CD, D],共有元素的长度为0;
- ”ABCDA”的前缀为[A, AB, ABC, ABCD],后缀为[BCDA, CDA, DA, A],共有元素为”A”,长度为1;
- ”ABCDAB”的前缀为[A, AB, ABC, ABCD, ABCDA],后缀为[BCDAB, CDAB, DAB, AB, B],共有元素为”AB”,长度为2;
- ”ABCDABD”的前缀为[A, AB, ABC, ABCD, ABCDA, ABCDAB],后缀为[BCDABD, CDABD, DABD, ABD, BD, D],共有元素的长度为0。
字符串匹配朴素做法

//cpp
int ViolentMatch(string& s, string& p)//s为长字符串,p为短字符串。
{
    for (int i = 0; i < m;)
    {
        int start = i, j = 0;//star 记录此次s中的开始位置
        while (i < m && j < n && s[i++] == p[j++]);//一次比较,直到不相等,注意句尾带分号
        if (j == n)//如果p串比较完了,返回开始位置
        {
            cout << start;
            return 0;
        }
        i = start + 1;//i更新到本次起始位置的下一个位置
    }
    cout << -1;
    return -1;
}

模拟

8. 字符串转换整数 (atoi)

请你来实现一个 atoi 函数,使其能将字符串转换成整数。

class Solution {
public:
    int myAtoi(string str) {
        int i = 0, flag = 1;
        long res = 0; //默认flag = 1,正数
        while (str[i] == ' ') i ++;
        if (str[i] == '-') flag = -1;
        if (str[i] == '-' || str[i] == '+') i ++;
        for (; i < str.size() && isdigit(str[i]); i ++)  {
            res = res * 10 + (str[i] - '0');
            if (res >= INT_MAX && flag == 1) return  INT_MAX;
            if (res > INT_MAX && flag == -1) return  INT_MIN;
        }
        return flag * res;
    }
};

415. 字符串相加

给定两个字符串形式的非负整数 num1 和num2 ,计算它们的和。

class Solution {
public:
    string addStrings(string num1, string num2) {
       int len1 = num1.length();
       int len2 = num2.length();
       for (int i = 0; i < len1 / 2; i ++){
           swap(num1[i], num1[len1 - i - 1]);
       }
       for (int j = 0; j < len2 / 2; j ++){
           swap(num2[j], num2[len2 - j - 1]);
       }
       string res = ""; int sign = 0;
       int t = 0;
       int p = 0, q = 0;
       while (p < len1 && q < len2){
           t = (num1[p] - '0') + (num2[q] - '0') + sign;
           if (t >= 10){
               t = t - 10;
               sign = 1;
           }else{
               sign = 0;
           }
           res += (char)(t + '0');
           p ++; q ++;
       }
       while (p < len1 ){
           t = (num1[p] - '0') + sign;
           if (t >= 10){
               t = t - 10;
               sign = 1;
           }else{
               sign = 0;
           }
           res += (char)(t + '0');
           p ++; 
       }
       while (q < len2){
           t = (num2[q] - '0')+ sign;
           if (t >= 10){
               t = t - 10;
               sign = 1;
           }else{
               sign = 0;
           }
           res += (char)(t + '0');
           q ++;
       }
       if (sign == 1) res +='1';
       int len = res.length();
       for (int i = 0; i < len/2; i ++){
           swap(res[i], res[len - i - 1]);
       }
            return res;
    }
};

12. 整数转罗马数字

class Solution {
public:
    string intToRoman(int num) {
        string result = "";
        int a[] = {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1};
        string b[] = {"M","CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX","V", "IV", "I"};
        int index = 0, cnt;
        while (num != 0){
            cnt = num / a[index];
            while (cnt --){
                result += b[index];
            }
            num = num % a[index];
            index ++;
        }
        return result;
    }
};

滑动窗口

常见模型:找出滑动窗口中的最大值/最小值
int hh = 0, tt = -1;
for (int i = 0; i < n; i ++ )
{
    while (hh <= tt && check_out(q[hh])) hh ++ ;  // 判断队头是否滑出窗口
    while (hh <= tt && check(q[tt], i)) tt -- ;
    q[ ++ tt] = i;
}

3. 无重复字符的最长子串

给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。

class Solution {
public:
int lengthOfLongestSubstring(string s) {
    vector<int> m(128, 0); //ASCII码范围:0-127
    int ans = 0;
    int i = 0;
    for (int j = 0; j < s.size(); j++) {
        if(m[s[j]]!=0)  i = max(i, m[s[j]]);
        m[s[j]] = j + 1;
        ans = max(ans, j - i + 1);
    }
    return ans;
    }
};
//s[j] 是新遇到的字母, m 是记录字母对应的新位置,所以 m[s[j]] 就是新字母对应的新位置

在这里插入图片描述
思路

  • j 表示子串的最后一个字母,计算子串长度为 j - i + 1
  • 滑动窗口,保证每个窗口里字母都是唯一的
  • 使用 vector m 来记录一个字母如果后面出现重复时,i 应该调整到的新位置
    所以每次更新的时候都会保存 j + 1 ,即字母后面的位置

1446. 连续字符

给你一个字符串 s ,字符串的「能量」定义为:只包含一种字符的最长非空子字符串的长度。

class Solution {
public:
    int maxPower(string s) {
    int ans = 0;
    int i = 0;
    for (int j = 0; j < s.size(); j++) {
        i = j;
        while (s[i] == s[j]) j++;
        j -- ;
        ans = max(ans, j - i + 1 );
    }
    return ans;
    }
};

请你返回字符串的能量。

其他类型

9. 回文数

判断一个整数是否是回文数。回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。

//判断回文数(串)
class Solution {
public:
    bool isPalindrome(int x) {
        string tmp=to_string(x);
        string tmp2=tmp;
        reverse(tmp2.begin(),tmp2.end());
        if(tmp==tmp2) return true;
        return false; 
    }
};
-------------------------
class Solution {
public:
  bool isPalindrome(int x) {
        if(x < 0)
            return false;
        int cur = 0;
        int num = x;
        while(num != 0) {
            cur = cur * 10 + num % 10;
            num /= 10;
        }
        return cur == x;
    }
}

151. 翻转字符串里的单词

给定一个字符串,逐个翻转字符串中的每个单词。

说明:
无空格字符构成一个 单词 。
输入字符串可以在前面或者后面包含多余的空格,但是反转后的字符不能包括。
如果两个单词间有多余的空格,将反转后单词间的空格减少到只含一个

class Solution {
public:
    string reverseWords(string s) 
    {
        stack<string> stk;
        string res, temp;
        istringstream ss(s); // ss与数输入的字符串s绑定
        while (ss >> temp)   // 以空格为分隔符将其分割
        {
            stk.push(temp);
            stk.push(" ");
        }
        if(!stk.empty()) 
            stk.pop();       // 弹出最后压入的那个多余空格
        while (! stk.empty())// 单词翻转后的新串
        {
            res += stk.top();
            stk.pop();
        }
        return res;
    }
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值