这篇文章主要介绍字符串相关的题目。
处理字符串操作相关问题时,常见的做法是从字符串尾部开始编辑,从后往前逆向操作。这么做的原因是因为字符串的尾部往往有足够空间,可以直接修改而不用担心覆盖字符串前面的数据。
摘自《程序员面试金典》
问题描述:
对于一个给定的源字符串和一个目标字符串,你应该输出在这个源字符串中匹配到的第一个索引。如果源字符串中不存在目标字符串,就返回-1.
例如:
源字符串为“source”和目标字符串为"target",就返回-1;
源字符串为“abcdabcdefg”和目标字符串为"bcd",就返回1;
题解
对于字符串查找问题,可使用双重 for 循环解决,效率更高的则为 KMP 算法。双重 for 循环的使用较有讲究,因为这里需要考虑目标字符串比源字符串短的可能。对目标字符串的循环肯定是必要的,所以可以优化的地方就在于如何访问源字符串了。简单直观的解法是利用源字符串的长度作为 for 循环的截止索引,这种方法需要处理源字符串中剩余长度不足以匹配目标字符串的情况,而更为高效的方案则为仅遍历源字符串中有可能和目标字符串匹配的部分索引。
Python
class Solution:
def strStr(self, source, target):
if source is None or target is None:
return -1
for i in range(len(source) - len(target) + 1):
for j in range(len(target)):
if source[i + j] != target[j]:
break
else: # no break
return i
return -1
C
int strStr(char* haystack, char* needle) {
if (haystack == NULL || needle == NULL) return -1;
const int len_h = strlen(haystack);
const int len_n = strlen(needle);
for (int i = 0; i < len_h - len_n + 1; i++) {
int j = 0;
for (; j < len_n; j++) {
if (haystack[i+j] != needle[j]) {
break;
}
}
if (j == len_n) return i;
}
return -1;
}
C++
class Solution {
public:
int strStr(string haystack, string needle) {
if (haystack.empty() && needle.empty()) return 0;
if (haystack.empty()) return -1;
if (needle.empty()) return 0;
// in case of overflow for negative
if (haystack.size() < needle.size()) return -1;
for (int i = 0; i < haystack.size() - needle.size() + 1; i++) {
string::size_type j = 0;
for (; j < needle.size(); j++) {
if (haystack[i + j] != needle[j]) break;
}
if (j == needle.size()) return i;
}
return -1;
}
};
Java
public class Solution {
public int strStr(String haystack, String needle) {
if (haystack == null && needle == null) return 0;
if (haystack == null) return -1;
if (needle == null) return 0;
for (int i = 0; i < haystack.length() - needle.length() + 1; i++) {
int j = 0;
for (; j < needle.length(); j++) {
if (haystack.charAt(i+j) != needle.charAt(j)) break;
}
if (j == needle.length()) return i;
}
return -1;
}
}
源码分析
haystack(source)
和needle(target)
有可能是空串。i
的循环判断条件,如果用的是i < source.length()
则在后面的source.charAt(i + j)
时有可能溢出。==
两边应加空格s1``s2
这类,要有意义,如target``source
i
,j
,这个视情况而定,如果需要在循环外再使用时,则须在外部初始化,否则没有这个必要。substring
方法。Python3 中用range
替换了xrange
,Python2 中使用xrange
效率略高一些。 另外需要注意的是 Python 代码中的else
接的是for
而不是if
, 其含义为no break
, 属于比较 Pythonic 的用法。复杂度分析
双重 for 循环,时间复杂度最坏情况下为 O((n−m)∗m)O((n-m)*m)O((n−m)∗m).
题解1-hashmap 统计字频
判断两个字符串是否互为变位词,若区分大小写,考虑空白字符时,直接来理解可以认为两个字符串的拥有各不同字符的数量相同。对于比较字符数量的问题常用的方法为遍历两个字符串,统计其中各字符出现的频次,若不等则返回false
. 有很多简单字符串类面试题都是此题的变形题。
Python
class Solution:
"""
@param s: The first string
@param b: The second string
@return true or false
"""
def anagram(self, s, t):
return collections.Counter(s) == collections.Counter(t)
class Solution {
public:
/**
* @param s: The first string
* @param b: The second string
* @return true or false
*/
bool anagram(string s, string t) {
if (s.empty() || t.empty()) {
return false;
}
if (s.size() != t.size()) {
return false;
}
int letterCount[256] = {0};
for (int i = 0; i != s.size(); ++i) {
++letterCount[s[i]];
--letterCount[t[i]];
}
for (int i = 0; i != t.size(); ++i) {
if (letterCount[t[i]] != 0) {
return false;
}
}
return true;
}
};
letterCount
数组的值,小于0时返回false
.for
循环做进一步优化,即t.size() > 256
时,使用256替代t.size()
, 使用i
替代t[i]
.
class Solution:
"""
@param s: The first string
@param b: The second string
@return true or false
"""
def anagram(self, s, t):
return sorted(s) == sorted(t)
class Solution {
public:
/**
* @param s: The first string
* @param b: The second string
* @return true or false
*/
bool anagram(string s, string t) {
if (s.empty() || t.empty()) {
return false;
}
if (s.size() != t.size()) {
return false;
}
sort(s.begin(), s.end());
sort(t.begin(), t.end());
if (s == t) {
return true;
} else {
return false;
}
}
};
s == t
时间复杂度最坏为
O(n)O(n)O(n)
.