一、字符串基础知识
学习链接 字符串匹配:
- 单模式串匹配问题(Single Pattern Matching)
- 多模式串匹配问题(Single Pattern Matching)
二、125. 验证回文串
题目: 给定一个字符串,验证它是否是回文串,只考虑字母和数字字符,可以忽略字母的大小写。
说明:本题中,我们将空字符串定义为有效的回文串。
示例: 输入: “A man, a plan, a canal: Panama” 输出: true 解释:“amanaplanacanalpanama” 是回文串
思路1: 将字符串处理一下,去掉空格和标点符号,保留字母和数字。 将处理好的字符串翻转对比一下即可。
class Solution:
def isPalindrome(self, s: str) -> bool:
s_r= ""
for s1 in s:
if s1.isalnum():
s_r = s_r + s1.lower()
return s_r == s_r[::-1]
思路2: 对撞指针。
class Solution:
def isPalindrome(self, s: str) -> bool:
left = 0
right = len(s)-1
while left < right:
if not s[left].isalnum():
left += 1
continue
if not s[right].isalnum():
right -= 1
continue
if s[left].lower() == s[right].lower():
right -= 1
left += 1
else:
return False
return True
三、0344.反转字符串
题目: 编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 s 的形式给出。
不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。
示例:
思路: 使用双指针,原地转换。
class Solution:
def reverseString(self, s: List[str]) -> None:
"""
Do not return anything, modify s in-place instead.
"""
left = 0
right = len(s)-1
while left < right:
temp = s[right]
s[right] = s[left]
s[left] = temp
right -= 1
left += 1
四、单模式串匹配
暴力BF算法: 最坏的时间复杂度为
O
(
m
?
n
)
O(m*n)
O(m?n) 最佳的时间复杂度为
O
(
m
)
O(m)
O(m)
RK(Rabin Karp)算法: 利用滚动哈希的思想,将
O
(
m
)
O(m)
O(m)降为
O
(
1
)
O(1)
O(1),从而RK算法的时间复杂度为
O
(
n
)
O(n)
O(n)。 但出现哈希冲突时,算法的效率会降低,最差的情况将退化成
O
(
m
?
n
)
O(m*n)
O(m?n)。
KMP(Knuth Morris Pratt)算法 通过构造前缀表,当匹配失败时,只回退模式串即可。 复杂度: 构造前缀表的复杂度为
O
(
m
)
O(m)
O(m)。 匹配阶段的复杂度为
O
(
n
)
O(n)
O(n)。 因此,总的复杂度为
O
(
m
+
n
)
O(m+n)
O(m+n)
五、0028.实现 strStr()
题目: 实现 strStr() 函数。 给你两个字符串 haystack 和 needle ,请你在 haystack 字符串中找出 needle 字符串出现的第一个位置(下标从 0 开始)。如果不存在,则返回 -1 。 说明: 当 needle 是空字符串时,我们应当返回什么值呢?这是一个在面试中很好的问题。 对于本题而言,当 needle 是空字符串时我们应当返回 0 。这与 C 语言的 strstr() 以及 Java 的 indexOf() 定义相符。
示例: 输入:haystack = “hello”, needle = “ll” 输出:2
思路1: 使用暴力法进行求解,即前面提到的BF算法:
class Solution:
def strStr(self, haystack: str, needle: str) -> int:
if needle == "":
return 0
n,m = len(haystack),len(needle)
i,j = 0,0
while i<n and j<m:
if haystack[i] == needle[j]:
i += 1
j += 1
else:
i = i - j + 1
j = 0
if j == m:
return i - j
else:
return -1
思路2: 也可以使用RK算法和KMP算法进行求解。
六、0796.旋转字符串
题目: 给定两个字符串, s 和 goal。如果在若干次旋转操作之后,s 能变成 goal ,那么返回 true 。 s 的 旋转操作 就是将 s 最左边的字符移动到最右边。 例如, 若 s = ‘abcde’,在旋转一次之后结果就是’bcdea’ 。
示例: 输入: s = “abcde”, goal = “cdeab” 输出: true
思:1: 首先,如果原始字符串s和目标字符串goal的长度不等,则任何的旋转都无法使二者相等。 接着,将原始字符串首位拼接,即s+s,就包括了字符串s循环一圈所有的可能性。
class Solution:
def rotateString(self, s: str, goal: str) -> bool:
if len(s) != len(goal):
return False
if goal in s+s:
return True
else:
return False
这里偷懒,使用了内置的字符串匹配。
思路2: 自己实现了一下kmp算法,效率有点低hhh
class Solution:
def rotateString(self, s: str, goal: str) -> bool:
if len(s)!=len(goal):
return False
flag = self.kmp(s+s,goal)
if flag == -1:
return False
else:
return True
def kmp(self,T:str,p:str) -> int:
n,m = len(T),len(p)
next = self.generateNext(p)
j = 0
for i in range(n):
while j>0 and T[i] != p[j]:
j = next[j-1]
if T[i] == p[j]:
j += 1
if j==m:
return i-m+1
return -1
def generateNext(self,p:str):
m = len(p)
next = [0 for _ in range(m)]
left = 0
for right in range(1,m):
while left > 0 and p[left]!=p[right]:
left = next[left-1]
if p[left] == p[right]:
left += 1
next[right] = left
return next
七、0208. 实现 Trie (前缀树)
题目: Trie(发音类似 “try”)或者说 前缀树 是一种树形数据结构,用于高效地存储和检索字符串数据集中的键。这一数据结构有相当多的应用情景,例如自动补完和拼写检查。
请你实现 Trie 类: Trie() 初始化前缀树对象。 void insert(String word) 向前缀树中插入字符串 word 。 boolean search(String word) 如果字符串 word 在前缀树中,返回 true(即,在检索之前已经插入);否则,返回 false 。 boolean startsWith(String prefix) 如果之前已经插入的字符串 word 的前缀之一为 prefix ,返回 true ;否则,返回 false 。
示例: 输入 [“Trie”, “insert”, “search”, “search”, “startsWith”, “insert”, “search”] [[], [“apple”], [“apple”], [“app”], [“app”], [“app”], [“app”]] 输出 [null, null, true, false, true, null, true]
解释 Trie trie = new Trie(); trie.insert(“apple”); trie.search(“apple”); // 返回 True trie.search(“app”); // 返回 False trie.startsWith(“app”); // 返回 True trie.insert(“app”); trie.search(“app”); // 返回 True
思路: 按照字典树的定义,编写代码即可。 参考链接
class Node:
def __init__(self):
self.children = dict()
self.isEnd = False
class Trie:
def __init__(self):
self.root = Node()
def insert(self, word: str) -> None:
cur = self.root
for ch in word:
if ch not in cur.children:
cur.children[ch] = Node()
cur = cur.children[ch]
cur.isEnd = True
def search(self, word: str) -> bool:
cur = self.root
for ch in word:
if ch not in cur.children:
return False
cur = cur.children[ch]
return cur is not None and cur.isEnd
def startsWith(self, prefix: str) -> bool:
cur = self.root
for ch in prefix:
if ch not in cur.children:
return False
cur = cur.children[ch]
return cur is not None
插入操作:直接从根节点开始遍历即可,没找到字符就新建一个Node()
查找操作:直接从根节点开始遍历,找到后要判断是不是在这里有一个isEnd标记。
查找前缀操作:直接从根节点开始遍历,找到后无需判断isEnd标记。
八、211. 添加与搜索单词 - 数据结构设计
题目: 请你设计一个数据结构,支持 添加新单词 和 查找字符串是否与任何先前添加的字符串匹配 。
实现词典类 WordDictionary :
WordDictionary() 初始化词典对象 void addWord(word) 将 word 添加到数据结构中,之后可以对它进行匹配 bool search(word) 如果数据结构中存在字符串与 word 匹配,则返回 true ;否则,返回 false 。word 中可能包含一些 ‘.’ ,每个 . 都可以表示任何一个字母。
示例: 输入: [“WordDictionary”,“addWord”,“addWord”,“addWord”,“search”,“search”,“search”,“search”] [[],[“bad”],[“dad”],[“mad”],[“pad”],[“bad”],[“.ad”],[“b…”]] 输出: [null,null,null,null,false,true,true,true]
解释: WordDictionary wordDictionary = new WordDictionary(); wordDictionary.addWord(“bad”); wordDictionary.addWord(“dad”); wordDictionary.addWord(“mad”); wordDictionary.search(“pad”); // 返回 False wordDictionary.search(“bad”); // 返回 True wordDictionary.search(“.ad”); // 返回 True wordDictionary.search(“b…”); // 返回 True
class Trie:
def __init__(self):
self.children = dict()
self.isEnd = False
def insert(self, word: str) -> None:
cur = self
for ch in word:
if ch not in cur.children:
cur.children[ch] = Trie()
cur = cur.children[ch]
cur.isEnd = True
def search(self, word: str) -> bool:
cur = self
for i in range(len(word)):
ch = word[i]
if ch == ".":
for node in cur.children.values():
if node.search(word[i+1:]):
return True
return False
elif ch not in cur.children:
return False
cur = cur.children[ch]
return cur.isEnd
class WordDictionary:
def __init__(self):
self.tree = Trie()
def addWord(self, word: str) -> None:
self.tree.insert(word)
def search(self, word: str) -> bool:
return self.tree.search(word)
这个题超时了…暂时不知道怎么解决。
|