本篇为Leetcode做题过程中字符串篇的总结和纠错等记录。
分类:
- 字符
- 回文串的定义
- 公共前缀
- 单词
- 字符串的反转
- 字符的统计
- 数字与字符串间转换
- 子序列
- 高精度运算
- 字符串变换
- 字符串匹配
- 中心拓展法
Python
要用Python做字符串类型的题,首先要了解Python内置了哪些处理字符串的函数或方法。
isalnum()
如果string至少有一个字符并且所有字符都是字母或数字则返回True,否则返回False。
filter()
用于过滤序列,过滤掉不符合条件的元素,返回由符合条件元素组成的新列表。该接收两个参数,第一个为函数,第二个为序列,序列的每个元素作为参数传递给函数进行判断,将返回True的元素放到新列表中。
join()
用于将序列中的元素以指定的字符连接生成一个新的字符串。用法为:
1 | str.join(item) # 括号里只能有一个成员 |
str[::-1] 字符反转
strip()
去除字符串开头或者结尾的空格
get()
字典的方法,返回指定键的值。
get(key, value) value可选,当键不在字典中返回value。默认返回None。
统计字符串中字符的数量:for c in p: obj[c] = obj.get(c,0) + 1
Java
S.toCharArray()
将字符串转为对应的字符串数组
S.split(“-“)
将字符串按字符’-‘分隔
Integer.toBinaryString()
Integer类的toBinaryString()方法将整数参数的字符串表示形式返回为二进制基数为2的无符号整数。
Integer.parseInt()
parseInt()方法用于将字符串参数作为有符号的十进制整数进行解析。
String.join()
用给定的定界符连接所有元素,例如a是String[],String.join("-",a);
计算字符串重新排列数
给定一个只包含大写英文字母的字符串S,要求你给出对S重新排列的所有不相同的排列数。
算法:
统计字符串中每个字符的出现次数。
计算字符总数的阶乘,这表示所有字符的排列数。
计算每个字符重复的次数的阶乘,并将这些阶乘相乘。
得到的结果就是不同排列的总数。
1 | from collections import Counter |
counter.values()返回Counter对象中所有计数的值,即字符串中每个字符的出现次数。
字符的统计
1 | for i in m: |
Java charAt()方法
- 用于返回指定索引处的字符
反转字符串
交换两个值:
1 | a,b = b,a |
s[:] = s[::-1] 是切片赋值语法,表示用s[::-1]替换s中的元素,注意不能写成s = s[::-1],因为s是形参,修改s不会影响函数外部传入的实参。注意这不是原地操作,需要O(n)的额外空间。
字符串算法篇
字符串匹配算法
KMP算法
例:28.找出字符串第一个匹配项的下标
给你两个字符串 haystack
和 needle
,请你在 haystack
字符串中找出 needle
字符串的第一个匹配项的下标(下标从 0 开始)。如果 needle
不是 haystack
的一部分,则返回 -1
。
示例 1:
1 | 输入:haystack = "sadbutsad", needle = "sad" |
示例 2:
1 | 输入:haystack = "leetcode", needle = "leeto" |
提示:
1 <= haystack.length, needle.length <= 104
haystack
和needle
仅由小写英文字符组成
KMP算法是一个快速查找匹配串的算法,作用:快速在原字符串中找到匹配字符串
在朴素解法O(m*n)的基础上将复杂度提高到仅有O(m+n)。核心:KMP算法能在[非完全匹配]的过程中提取到有效信息进行复用,以减少[重复匹配]的消耗。
朴素匹配的逻辑:将原串的指针移动到本次[发起点]的下一个位置,匹配串的指针移动到起始位置;尝试匹配,发现对不上,原串的指针会一直往后移动,直到能够与匹配串对上位置。
所以朴素匹配的复杂度是O(m*n)
KMP匹配过程:
1、匹配串检查之前已经匹配成功的部分中是否存在相同的前缀和后缀,如果存在,则跳转到前缀的下一个位置继续往下匹配
2、跳转后尝试匹配,发现两个指针的字符对不上,并且匹配串指针前面不存在相同的前缀和后缀,这时候就回到匹配串的起始位置重新开始
KMP比朴素解法更快的原因:
- KMP利用已匹配部分中相同的前缀和后缀来加速下一次的匹配
- KMP的原串指针不进行回溯,没有朴素匹配中回到下一个发起点的过程
可以优化的部分只能是 检查已匹配部分的相同前缀和后缀 这一过程。检查前缀和后缀的目的其实是 为了确定匹配串中的下一段开始匹配的位置。
对于匹配串的任意一个位置,由该位置发起的下一个匹配点位置其实与原串无关。
预处理得到next数组,数组中每个位置的值就是该下标应该跳转的目标位置(next点)
1 | std::vector<int> KMP(const std::string &text, const std::string &pattern) { |
1 | def kmp_search(string, patt): |
时间复杂度为O(n),n代表主串的长度
next数组的构建:
所以next数组的本质就是寻找子串中相同前后缀的长度,并且一定是最长的前后缀
1 | def build_next(patt): |
计算Next数组的本质还是通过已经掌握的信息来规避重复的运算,和动态规划的思想很像