CC150-Chapter 1 数组与字符串
刷leetcode遇到瓶颈之后,开始打算分类学习面试中的算法,于是就开始学习Cracking the Coding Interview第六版,并且配合其中的练习题,主要用Java或python实现,以下是对第一章中题目的总结。
1.1 判断字符串中字符互异
题目描述:实现一个算法,确定一个字符串中的所有字符是否全都不同。如果不允许使用额外的存储结构。
解法1:首先确定组成字符串的字符集是ASCII还是Unicode。假设由ASCII字符组成,先判断字符串长度是否大于128(字符集的长度),若大于则一定存在重复字符。然后创建布尔类型的数组char_set[128]记录出现在字符串中的字符。当扫描到某一字符时,若char_set中对应为TRUE,表示该字符已出现在字符串中,返回FALSE。
因为需要扫描一遍字符串,故时间复杂度为O(n),空间复杂度为O(1)。
解法2:不允许使用额外存储结构的情况下,从前往后扫描字符串,比较每一个字符和其后面所有的字符是否相同。时间复杂度为O(n^2)。
def checkDifferent(self, iniString):
for i in xrange(len(iniString)):
for j in xrange(i+1, len(iniString)):
if iniString[i] == iniString[j]:
return False
return True
1.2 判断两个字符串是否乱序同构
题目描述:给定两个字符串,判断其中一个字符串的字符重新排列后,能否变成另一个字符串。
思路:首先需要注意一些细节,例如大小写是否敏感,是否需要考虑空格。根据同构的定义,只需要判断两个字符串中出现的字符及其出现次数是否一致。
解法1:用字典记录字符串中出现的字符及其出现次数,但代码运行时间较长。
def checkSam(self, stringA, stringB):
tmp = {}
for i in stringA:
if not tmp or i not in tmp:
tmp[i] = 1
else:
tmp[i] += 1
for j in stringB:
if j in tmp:
tmp[j] -= 1
else:
return False #StringB中的字符在StringA中没有出现
for d in tmp:
if tmp[d] > 0:
return False #两个字符串中字符出现次数不等
return True
解法2:类似1.1中创建int类型的数组A[128]和B[128]分别记录A、B串中字符出现的次数并比较。首先通过判断字符串长度不等时不存在同构来优化。
public boolean checkSam(String stringA, String stringB) {
if(stringA.length() != stringB.length())
return false;
int[] A = new int[128];
int[] B = new int[128];
for(int i = 0; i < stringA.length(); i++)
{
A[stringA.charAt(i)]++;
}
for(int i = 0; i < stringA.length(); i++)
{
B[stringB.charAt(i)]++;
}
for(int i = 0; i < 128; i++)
{
if(A[i] != B[i])
return false;
}
return true;
}
1.3 URLify(字符替换)
题目描述:将字符串中的空格全部替换为“%20”。假定该字符串有足够的空间存放新增的字符,并且知道字符串的真实长度。
解法:利用String类的replace方法很直接,但会返回一个新的String,如果要求在原有字符串中修改,则需要使用char数组。
public String replaceSpace(String iniString, int length) {
return iniString.replace(" " , "%20");
}
1.4 判断字符串是否可排列为回文序列
题目描述:判断一个给定的字符串是否可通过重新排列成为一个回文字符串。
思路:回文序列即从前往后和从后往前遍历是一样的。从字符出现的次数上来分析,如果至多只有一个字符出现的次数为奇数次,则该字符串可排列为回文序列,因为其余的字符可平均分配在字符串的两侧。
1.5 字符串压缩
题目描述:利用字符重复出现的次数,实现字符串压缩功能。例如字符串“aabcccccaaa”经压缩会变成“a2b1c5a3”。若压缩后的字符串没有变短,则返回原先的字符串。
解法:
public String zipString(String iniString) {
String tmp = "";
int count = 0;
for(int i = 0; i < iniString.length(); i++)
{
if(i==0||iniString.charAt(i)!= niString.charAt(i-1))
{
if(count > 0)
tmp += count;
tmp += iniString.charAt(i);
count = 1;
}
else
count += 1;
}
tmp += count;
return tmp.length() < iniString.length()?tmp : iniString;
}
书中提到用StringBuilder可以更加优化,但对此不是很熟悉,后续再补。
1.6 矩阵90度旋转(同LeetCode 48)
题目描述:有一副由NxN矩阵表示的图像,每个像素用一个int表示,在不占用额外内存空间的情况下(即不使用缓存矩阵),将图像顺时针旋转90度。
解法: 从最外层开始,向内逐层翻转矩阵元素。
def rotate(self, matrix):
start = 0
end = len(matrix) - 1
while start < end: #控制每一层开始和结束的元素位置
for i in range(start, end):
tmp = matrix[i][end]
#top -> right
matrix[i][end] = matrix[start][i]
#left -> top
matrix[start][i] = matrix[end-i+start][start]
#bottom -> left
matrix[end-i+start][start] = matrix[end][end-i+start]
#right -> bottom
matrix[end][end-i+start] = tmp
start += 1
end -= 1
1.7 零矩阵(同LeetCode 73)
题目描述:若m*n矩阵中某个元素为0,则将其所在的行与列全部清零。
解法1: 遍历矩阵,存储矩阵元素为0的行号与列号,再将对应的行列元素全部置为0。
def setZeroes(self, matrix):
tmp = []
n = len(matrix)
m = len(matrix[0])
for i in xrange(n):
for j in xrange(m):
if matrix[i][j] == 0:
tmp.append([i, j])
for index in tmp:
for i in xrange(m):
matrix[index[0]][i] = 0
for j in xrange(n):
matrix[j][index[1]] = 0
解法2: 直接用矩阵的第一行和第一列记录某列(行)中是否有零元素。
public void setZeroes(int[][] matrix) {
boolean fr = false; //fr(fc)记录原有的第一行(列)是否有零元素
boolean fc = false;//因为第一行或第一列的零元素都会由[0][0]记录
for(int i = 0; i < matrix.length; i++)
{
for(int j = 0; j < matrix[0].length; j++)
{
if(matrix[i][j] == 0)
{
if(i == 0)
fr = true;
if(j == 0)
fc = true;
matrix[i][0] = matrix[0][j] = 0;
}
}
}
for(int i = 1; i < matrix.length; i++)
{
for(int j = 1; j < matrix[0].length; j++)
{
if(matrix[i][0] == 0 || matrix[0][j] == 0)
matrix[i][j] = 0;
}
}
if(fr)
{
for(int i = 0; i < matrix[0].length; i++)
matrix[0][i] = 0;
}
if(fc)
{
for(int i = 0; i < matrix.length; i++)
matrix[i][0] = 0;
}
}
1.8 字符串翻转
题目描述:假定已知isSubstring方法来检查一个单词是否为其他字符串的子串。给定两个字符串s1和s2,判断s2是否为s1旋转而成,要求只能调用一次isSubstring方法。
解法: 仅需要判断是否可将字符串s1分隔为两部分x和y,使得 xy = s1且 yx = s2。无论分割点在哪里,都存在yx是xyxy的子串,即s2为s1s1的子串。
public boolean checkReverseEqual(String s1, String s2) {
if(s1.length() != s2.length())
return false;
String s3 = s1 + s1;
return s3.contains(s2);
}