leetcode字符串专题

- 日理万妓 2023-06-13 11:19 294阅读 0赞

字符串专题

文章目录

  • 字符串专题
    • 一,字符串匹配算法
        • 暴力匹配BF
        • KMP字符串匹配
    • 二,leetcode字符串题解
      • 统计词频相关
        • 1.赎金信
        • 2.字符串中第一个唯一字符
        • 3.学生出勤记录
      • 字符串反转相关
        • 1.反转字符串I
        • 2.反转字符串II
      • 回文串相关
        • 1.验证回文串
        • 2.验证回文串II
        • 3.最长回文子串
        • 4.回文字串

一,字符串匹配算法

所谓字符串匹配就是给定一个模式串和文本串,判定模式串是否是文本串的一个字串,并返回模式串在文本串中开始出现的位置

暴力匹配BF

  1. /**
  2. * 暴力解法
  3. * O(MN)
  4. *
  5. * @param txt
  6. * @param pat
  7. * @return
  8. */
  9. public static int forceSearch(String txt, String pat) {
  10. int txtLen = txt.length();
  11. int patLen = pat.length();
  12. int i = 0;
  13. int j = 0;
  14. while (i < txtLen && j < patLen) {
  15. //逐一比对
  16. if (txt.charAt(i) == pat.charAt(j)) {
  17. //比对成功,继续向后
  18. i++;
  19. j++;
  20. } else {
  21. //比对失败,j回到模式串开始,i回到上一次比对位置的下一个位置
  22. j = 0;
  23. i = i - j + 1;
  24. }
  25. }
  26. if (j == patLen) {
  27. return i - j;
  28. } else {
  29. return -1;
  30. }
  31. }

KMP字符串匹配

  1. public int kmpSearch(String txt, String pat, int[] next) {
  2. int txtLen = txt.length();
  3. int patLen = pat.length();
  4. int i = 0;
  5. int j = 0;
  6. while (i < txtLen && j < patLen) {
  7. if (j == -1 || txt.charAt(i) == pat.charAt(j)) {
  8. //匹配则继续比较下一个元素
  9. j++;
  10. i++;
  11. } else {
  12. //不匹配则将模式串移动到以对应next数组元素为索引的位置
  13. j = next[j];
  14. }
  15. }
  16. if (j == patLen) {
  17. return i - j;
  18. } else {
  19. return -1;
  20. }
  21. }
  22. /**
  23. * 寻找前缀后缀最长公共元素长度
  24. * 获取next数组,告知下一次应该跳到模式串的哪一个位置
  25. *
  26. * @param patStr
  27. * @param next
  28. */
  29. public void next(String patStr, int[] next) {
  30. int length = patStr.length();
  31. next[0] = -1;
  32. //指向前边公共前缀的指针
  33. int k = -1;
  34. //遍历时指向当前字符的指针
  35. int j = 0;
  36. //这里其实用到了动态规划的思想
  37. while (j < length - 1) {
  38. if (k == -1 || patStr.charAt(k) == patStr.charAt(j)) {
  39. ++k;
  40. ++j;
  41. next[j] = k;
  42. } else {
  43. k = next[k];
  44. }
  45. }
  46. }

二,leetcode字符串题解

统计词频相关

1.赎金信

在这里插入图片描述

其实这道题目的意思就是,给定两个字符串A和B,判断B中的的字符能否构成A,这时候我们只需要统计出B的字符和其出现的频次,然后在A中看能否够用即可

  1. public boolean canConstruct(String ransomNote, String magazine) {
  2. Map<Character,Integer> map = new HashMap<>();
  3. //统计magazine中各个字符出现的次数
  4. for(Character c : magazine.toCharArray()){
  5. map.put(c , map.getOrDefault(c,0) + 1);
  6. }
  7. //遍历过程中,判断map中有没有ransomNote需要字符,有则还需判断还够不够其所需
  8. for(Character c : ransomNote.toCharArray()){
  9. if(map.containsKey(c) && map.get(c) != 0){
  10. map.put(c,map.get(c) - 1);
  11. }else{
  12. return false;
  13. }
  14. }
  15. return true;
  16. }

2.字符串中第一个唯一字符

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QSspdxcw-1573918895663)(C:\Users\12642\AppData\Roaming\Typora\typora-user-images\image-20191116152348466.png)]

也是基于统计词频,返回第一个词频为1的即可

  1. /**
  2. * 字符串中第一个唯一字符
  3. * @param s
  4. * @return
  5. */
  6. public int firstUniqChar(String s) {
  7. Map<Character,Integer> map = new HashMap<>();
  8. //统计词频率
  9. for(Character c : s.toCharArray()){
  10. map.put(c,map.getOrDefault(c,0) + 1);
  11. }
  12. //按序遍历找出第一个唯一的字符
  13. for(Character c : s.toCharArray()){
  14. if(map.containsKey(c) && map.get(c) == 1){
  15. return s.indexOf(c);
  16. }
  17. }
  18. return -1;
  19. }

3.学生出勤记录

在这里插入图片描述

  1. public boolean checkRecord(String s) {
  2. //连续迟到2次以上
  3. if(s.indexOf("LLL") != -1){
  4. return false;
  5. }
  6. int Asum = 0;
  7. //统计词频率
  8. for(Character c : s.toCharArray()) {
  9. if(c == 'A'){
  10. Asum++;
  11. }
  12. //超过1个A
  13. if(Asum > 1){
  14. return false;
  15. }
  16. }
  17. return true;
  18. }

字符串反转相关

1.反转字符串I

在这里插入图片描述

双指针,一个从前往后,一个从后往前,边遍历边交换

  1. public void reverseString(char[] s) {
  2. //双指针法
  3. int start = 0;
  4. int end = s.length - 1;
  5. if(start == end){
  6. return;
  7. }
  8. while(start < end){
  9. char c = s[start];
  10. s[start] = s[end];
  11. s[end] = c;
  12. start++;
  13. end--;
  14. }
  15. }

2.反转字符串II

在这里插入图片描述

  1. public String reverseStr2(String s, int k) {
  2. int n = s.length();
  3. //反转轮次
  4. int time = 1;
  5. char[] chars = s.toCharArray();
  6. for(int i = 0;i < n;){
  7. //记录每一轮次的结束字符的索引
  8. int j = Math.min(n,time * 2 * k);
  9. //记录剩余字符个数
  10. int rest = j - i;
  11. if(rest >= k){
  12. //剩余字符多余k个,则反转前k个
  13. reverse(chars,i,i + k - 1);
  14. }else{
  15. //否则将剩余的所有全部反转
  16. reverse(chars,i,j - 1);
  17. }
  18. i = j;
  19. time++;
  20. }
  21. return String.valueOf(chars);
  22. }
  23. public void reverse(char[] s,int i,int j) {
  24. //双指针法
  25. int start = i;
  26. int end = j;
  27. if(start == end){
  28. return;
  29. }
  30. while(start < end){
  31. char c = s[start];
  32. s[start] = s[end];
  33. s[end] = c;
  34. start++;
  35. end--;
  36. }
  37. }

回文串相关

1.验证回文串

在这里插入图片描述

  1. /**
  2. * 验证回文串
  3. * @param s
  4. * @return
  5. */
  6. public boolean isPalindrome(String s) {
  7. s = s.toLowerCase();
  8. s = s.replaceAll("[^0-9a-z]","");
  9. int start = 0;
  10. int end = s.length() - 1;
  11. char[] chars = s.toCharArray();
  12. while(start < end){
  13. if(chars[start] != chars[end]){
  14. return false;
  15. }else{
  16. start++;
  17. end--;
  18. }
  19. }
  20. return true;
  21. }

2.验证回文串II

在这里插入图片描述

  1. public boolean validPalindrome(String s) {
  2. s = s.toLowerCase();
  3. s = s.replaceAll("[^0-9a-z]","");
  4. int start = 0;
  5. int end = s.length() - 1;
  6. char[] chars = s.toCharArray();
  7. while(start < end){
  8. if(chars[start] != chars[end]){
  9. return isValid(s,start,end - 1) || isValid(s,start + 1,end);
  10. }else{
  11. start++;
  12. end--;
  13. }
  14. }
  15. return true;
  16. }
  17. public boolean isValid(String s, int i, int j){
  18. while(i < j){
  19. if(s.charAt(i) != s.charAt(j)){
  20. return false;
  21. }
  22. i++;
  23. j--;
  24. }
  25. return true;
  26. }

3.最长回文子串

在这里插入图片描述

动态规划解法:

  1. /**
  2. * 最长回文子串
  3. * @param s
  4. * @return
  5. */
  6. public String longestPalindrome(String s) {
  7. int len = s.length();
  8. if(len <= 1){
  9. return s;
  10. }
  11. boolean[][] dp = new boolean[len][len];
  12. //记录最长回文串长度
  13. int longestPalindromeLen = 1;
  14. //记录最长的回文串
  15. String longestPalindrome = s.substring(0,1);
  16. //右边界
  17. for(int r = 1;r < len;r++){
  18. //左边界
  19. for(int l = 0;l < r;l++){
  20. //动态转移方程:
  21. // dp[l][r]:表示从l到r的字串是否是回文串
  22. // dp[l][r]如果是回文串的话那么 dp[l + 1][r - 1]必定是回文串
  23. // 还有一种情况,如果类似 aba这种,当我l+1,r-1区间收缩后,就只剩下b,b肯定是回文串,
  24. // 又或者类似aa这种,收缩后没有字符,也肯定是回文串,则说明,如果区间[l,r]之间少于1个元素,则肯定回文
  25. // 所以:r - l <= 2也回文
  26. // if(s[l] == s[r] &&( r - l <= 2 || dp[l + 1][r - 1])
  27. // dp[l][r] = true
  28. if(s.charAt(l) == s.charAt(r) && (r - l <= 2 || dp[l + 1][r - 1])){
  29. //改变状态
  30. dp[l][r] = true;
  31. //记录长度
  32. if(r - l + 1 > longestPalindromeLen){
  33. longestPalindromeLen = r - l + 1;
  34. longestPalindrome = s.substring(l,r + 1);
  35. }
  36. }
  37. }
  38. }
  39. return longestPalindrome;
  40. }

4.回文字串

在这里插入图片描述

暴力解法:

  1. /**
  2. * 回文字串数量
  3. * @param s
  4. * @return
  5. */
  6. public int countSubstrings(String s) {
  7. int len = s.length();
  8. //记录回文字串数量
  9. int res = 0;
  10. //暴力遍历判断所有字串,每一个子串区间[i,j]
  11. for(int i = 0;i < len;i++){
  12. for(int j = i;j < len;j++){
  13. boolean b = true;
  14. int ii = i;
  15. int jj = j;
  16. //判断是否回文
  17. while(ii < jj){
  18. if(s.charAt(ii) != s.charAt(jj)){
  19. b = false;
  20. break;
  21. }else{
  22. ii++;
  23. jj--;
  24. }
  25. }
  26. if(b){
  27. res++;
  28. }
  29. }
  30. }
  31. return res;
  32. }

动态规划解法:

  1. /**
  2. * 动态规划
  3. * @param s
  4. * @return
  5. */
  6. public int countSubstrings1(String s) {
  7. char[] charArr = s.toCharArray();
  8. int n = charArr.length;
  9. int count = 0;
  10. boolean dp[][] = new boolean[n][n];
  11. //从后往前遍历
  12. for (int i = n - 1; i >= 0; i--) {
  13. for (int j = i; j < n; j++) {
  14. if (charArr[i] == charArr[j] && (j - i <= 2 || dp[i + 1][j - 1])) {
  15. dp[i][j] = true;
  16. count++;
  17. }
  18. }
  19. }
  20. return count;
  21. }

发表评论

表情:
评论列表 (有 0 条评论,294人围观)

还没有评论,来说两句吧...

相关阅读

    相关 LeetCode 动态规划专题

    这是整理的LeetCode上的动态规划专题 背包问题 我们将LeetCode上的背包问题进行一个整理,希望之后再遇到以下问题能够快速解决。 组合问题 1. 如

    相关 LeetCode 二分专题

    这是整理的LeetCode上的二分专题,先按照codeTop上的频率顺序从高到低进行整理,然后按别人整理的进行再次整理。 二分搜索概述 先不从具体题来出发,而先从二分查

    相关 字符串专题(一)

    一直不太会字符串的处理,这周一定要攻克它。 str.toCharArray() : 将字符串转换为字符数组 【1】判断两个字符串是否互为变形词 题目:两个字符串str1