这里写目录标题
字符串
本章节代码部分参考自网络,部分由自己编写,但都已得到验证,均可正确执行。
0. 字符串去重
要求:
input:hellolo
out:helo
Python
# 方法 1
def filter1():
string = "hellolo"
res = ""
for i in range(len(string)):
if string[i] not in res:
res = res + string[i]
print(res)
# 方法二
# 使用set()中元素不能重复的特性。但是(去重后会进行排序)。
# 使用list()中元素可以重复的特性作为排序规则进行排序。
# key的作用:我们可以利用它实现按我们想要的标准进行排序,即我们可以自定义排序的标准,或者称为自定义排序的方式。
def filter2():
string = "hellolo"
list1 = list(string)
print(list1.index)
lists = list(set(list1))
lists.sort(key=list1.index)
print(lists)
java 代码
方法1
public String unique_string (String s) {
HashSet<Character> set = new HashSet<Character>();
StringBuffer stringBuffer= new StringBuffer();
for(int i = 0;i<s.length();i++) {
if (!set.contains(s.charAt(i))) {
set.add(s.charAt(i));
stringBuffer.append(s.charAt(i));
}
}
return stringBuffer.toString();
}
方法 2
package Solution;
public class Test {
public String filter(String s) {
if(s==null)
return null;
StringBuffer sb = new StringBuffer();
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if(s.indexOf(c) == s.lastIndexOf(c)) { 第一次出现的下标跟最后一次相等,表示当前字符没有出现重复
sb.append(c);
}
else {
if(s.indexOf(c) == i) {
sb.append(c);
} //如果重复出现的字符的位置等于当先字符的索引值,即表示当前字符为重复字符出现的第一次,将其加入StringBuilder中
}
}
return sb.toString();
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Test test = new Test();
String s = "hellolo";
System.out.println("过滤重复字符之后的字符串:");
System.out.println(test.filter(s));
}
}
1. 回文数:【Valid Palindrome】
给定一个字符串,确定它是否是一个回文,只考虑字母数字字符,忽略大小写。
例如:
- “A man, a plan, a canal: Panama” is a palindrome.
- “race a car” is not a palindrome.
注意:你是否考虑过这个字符串可能是空的?这是个很好的问题,可以在面试时问。在这个问题上,我们把空字符串定义为有效的回文。
Python
解析:
-
首先去掉空格其他字符,然后转换大小写.lower()
-
最后把字符转换过来,看看和原来的一不一样就可以了
def is_palindrome(s):
s1 = []
for i in range(len(s)):
# isalnum()检查一个字符串是否由字母或数字组成
if s[i].isalnum(): # 在这里我们用isalnum()来判断s[i]字符是不是数字和字母,用来去除其他符号
s1.append(s[i].lower())
if s1 == s1[::-1]: # 翻转后是否与之前的相等
return True
else:
return False
if __name__ == '__main__':
string = "A man, a plan, a canal: Panama"
print(is_palindrome(string))
方法二
def is_palindrome2(str):
l , r = 0 ,len(s)-1
while l<r:
while l<r and not s[l].isalnum(): # 不是数字和字符
l += 1
while l<r and not s[r].isalnum():
r -= 1
if s[l].lower() != s[r].lower(): # 判断对应指针字符是否相同
return False
l += 1
r -= 1
return True
java 代码
思路:
- 采用前后指针,不断往前移动即可
- 如果当前元素不是字母和数字,就跳过继续往前
public boolean isPalindrome(String s) {
s = s.toLowerCase(); // 把输入字符串中的大写字母全部转换为小写字符
int start = 0;
int end = s.length() - 1;
while (start < end) {
char left = s.charAt(start); // charAt()方法返回指定索引位置的char值
char right = s.charAt(end);
// 判断前后对应指针的字符是不是数字或者字符,用来去除其他符号
boolean leftFlag = check(left);
boolean rightFlag = check(right);
// 两个都是Ture
if (leftFlag && rightFlag) {
// 判断他们是否相同
if (left != right)
return false;
// 是,头指针加1,尾指针减一
start++;
end--;
} else {// 两个字符有一个不是Ture
if (!leftFlag) // 头指针字符不是数字和字符,头指针加1
start++;
if (!rightFlag) // 尾指针字符不是数字和字符,尾指针减1
end--;
}
}
return true;
}
public boolean check(char ch) {
if ((ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9'))
return true;
return false;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
String_Solution solution = new String_Solution();
String string = "A man, a plan, a canal: Panama";
boolean bool = solution.isPalindrome(string);
System.out.println(bool);
}
2. 字符串匹配【Implement strStr()】
给定一个 haystack 字符串和一个 needle 字符串,在 haystack 字符串中找出 needle 字符串出现的第一个位置 (从0开始)。如果不存在,则返回 -1。
例如
Input: haystack = "hello", needle = "ll"
Output: 2
Input: haystack = "aaaaa", needle = "bba"
Output: -1
Python
def find_substr(string, substring):
fri_index = string.find(substring, 0, len(string))
last_index = string.rfind(substring) # 0, len(string)默认
return fri_index, last_index
if __name__ == '__main__':
s = "this is string example....wow!!!"
subs = "is"
fri, last = find_substr(s, subs)
print("fri = {} last = {}".format(fri, last))
输出
fri = 2 last = 5
java代码
解析:
- 首先,needle字符串长度为0,此时按要求返回0.
- 正常情况下,使用两层循环,将needle字符串依次与haystack不同位置开始的与needle长度相同的子串进行比较。
- 外层循环次数为sizeA-sizeB;两者的长度差;
- 内层循环为 sizeB,needle字符串的长度;【即字串与长字符串匹配的次数】
- 使用一个标志位 flag,表示 haystack 中能否找到 needle 子串;找到,返回 idx;没找到,继续找。
public int strStr(String haystack, String needle) {
if (needle.isEmpty()) return 0;
int sizeA = haystack.length(), sizeB = needle.length();
for (int i = 0; i <= sizeA - sizeB; i++) {
if (haystack.substring(i, i + needle.length()).equals(needle))
return i;
}
return -1;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
String_Solution solution = new String_Solution();
String string = "aaaaa";
String string2 = "bba";
int bool = solution.strStr(string, string2);
System.out.println(bool);
}
方法二
// indexOf 字符串中查找其子串第一次出现的位置,如果没有找到该子串,则返回-1
public int strStr2(String haystack, String needle) {
int index=haystack.indexOf(needle);
return index;
}
3. Add Binary
给定两个二进制字符串,返回他们的和(用二进制表示)
def addbin(str1, str2):
"""
bin()返回一个整数 int 或者长整数 long int 的二进制表示。
int() 函数用于将一个字符串或数字转换为整型。int(x, base=10) x字符串或数字; base 进制数,默认十进制。
:param str1:
:param str2:
:return:
"""
print(bin(int(str1, 2) + int(str2, 2)))
return bin(int(str1, 2) + int(str2, 2))[2:]
if __name__ == '__main__':
s1 = "11"
s2 = "1"
c = addbin(s1, s2)
print(c)
输出:
0b100
100
4. 最长回文子序列【Longest Palindromic Substring】
思路:
- 暴力枚举法: BF朴素串模式匹配算法
- 中心扩散法,时间复杂度: O ( n 2 ) O(n^2) O(n2)
- 动态规划方法
- manacher 算法
Python
- 中心扩散法求解
def longest_palindrome(s):
"""
中心扩展法
step 1:遍历每个字符,把每个字符当做中心逐步向两边扩展,每扩展一步就得到一个新的子串。
这里针对输入字符串的长度,扩展方式需要根据长度奇偶性质做判断。
Step 2:判断子串是否为回文串,更新当前最长回文串
Step 3:返回最长回文串
"""
lens = len(s)
maxlen = 0
start = 0
# 长度为奇数 且情况特殊时:例如'aba'
for i in range(lens):
j = i - 1 # 以 i 位置为中心, j向左扩散
k = i + 1 # 以 i 位置为中心, j向右扩散
while j >= 0 and k < lens and s[j] == s[k]:
if k - j + 1 > maxlen:
maxlen = k - j + 1
start = j # j 为回文串的首个字符索引
j -= 1
k += 1
# 长度为偶数
for i in range(lens):
j = i
k = i + 1
while j >= 0 and k < lens and s[j] == s[k]:
if k - j + 1 > maxlen:
maxlen = k - j + 1
start = j
j -= 1
k += 1
if maxlen > 0:
return s[start:start + maxlen]
return None
if __name__ == '__main__':
s1 = "aba"
print(longest_palindrome(s1))
java 代码
- 动态规划求解最长回文串
对于一个子串而言,如果它是回文串,并且长度大于
2
2
2,那么将它首尾的两个字母去除之后,它仍
然是个回文串。
- 例如对于字符串 “ a b a b a ” “ababa” “ababa”, 如果我们已经知道 “ b a b ” “bab” “bab” 是回文串,那么 “ a b a b a ” “ababa” “ababa” 一定是回文串,这是因为它的首尾两个字母都是 “ a ” “a” “a”。
根据这样的思路,我们就可以用动态规划的方法解决本题。我们用 P ( i , j ) P(i, j) P(i,j) 表示字符串 s s s 的第 i i i 到 j j j 个字母组成的串(下文表示成 s [ i : j ] s[i: j] s[i:j]) 是否为回文串:
P
(
i
,
j
)
=
{
T
u
r
e
如果字串
S
i
...
S
j
是回文串
F
a
l
s
e
other
P(i,j)= \begin{cases} Ture& \text{如果字串$S_i$...$S_j是回文串$}\\ False& \text{other} \end{cases}
P(i,j)={TureFalse如果字串Si...Sj是回文串other
这里的「其它情况」包含两种可能性:
- s [ i , j ] s[i, j] s[i,j]本身不是一个回文串;
- i > j i > j i>j,此时 s [ i , j ] s[i,j] s[i,j]本身不合法。
那么我们就可以写出动态规划的状态转移方程:
P
(
i
,
j
)
=
P
(
i
+
1
,
j
−
1
)
∧
(
S
i
=
=
S
j
)
P(i,j)= P(i+1,j-1) \wedge (S_i== S_j)
P(i,j)=P(i+1,j−1)∧(Si==Sj)
也就是说,只有
s
[
i
+
1
:
j
−
1
]
s[i+1 :j - 1]
s[i+1:j−1] 是回文串,并且
s
s
s 的第
i
i
i 和
j
j
j 个字母相同时,
s
[
i
:
j
]
s[i: j]
s[i:j]才会是回文串。
上文的所有讨论是建立在子串长度大于2的前提之上的,我们还需要考虑动态规划中的边界条件,即子串的长度为1或2。对于长度为1的子串,它显然是个回文串;对于长度为2的子串,只要它的两个字母相同,它就是一个回文串。
因此我们就可以写出动态规划的边界条件:
{
P
(
i
,
i
)
=
T
u
r
e
P
(
i
,
i
+
1
)
=
(
S
i
=
=
S
j
)
\begin{cases} P(i,i)=Ture& \\ P(i,i+1)=(S_i==S_j) \end{cases}
{P(i,i)=TureP(i,i+1)=(Si==Sj)
根据这个思路,我们就可以完成动态规划了,最终的答案即为所有
P
(
i
,
j
)
=
t
r
u
e
P(i,j)= true
P(i,j)=true中
j
−
i
+
1
j - i + 1
j−i+1 (即子串长度)的最大值。
注意:在状态转移方程中,我们是从长度较短的字符串向长度较长的字符串进行转移的,因此一定要注意动态规划的循环顺序。
public String longestPalindromeDynamicProgramming(String s) {
int n = s.length();
boolean[][] dp = new boolean[n][n];
String ans = "";
for (int l = 0; l < n; ++l) {
for (int i = 0; i + l < n; ++i) {
int j = i + l;
if (l == 0) {
dp[i][j] = true;
} else if (l == 1) {
dp[i][j] = (s.charAt(i) == s.charAt(j));
} else {
dp[i][j] = (s.charAt(i) == s.charAt(j) && dp[i + 1][j - 1]);
}
if (dp[i][j] && l + 1 > ans.length()) {
ans = s.substring(i, i + l + 1);
}
}
}
return ans;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
String_Solution solution = new String_Solution();
String string = "aba";
System.out.println(solution.longestPalindromeDynamicProgramming(string));
}
5. 通配符匹配 【Wildcard Matching】
实现通配符模式匹配,支持 ‘?’ 和 ‘*’ 。‘? ’ 匹配任何单个字符。’*’ 匹配任何序列的字符(包括空序列)。匹配应该覆盖整个输入字符串(不是部分)。
例如
Some examples:
isMatch("aa","a") → false
isMatch("aa","aa") → true
isMatch("aaa","aa") → false
isMatch("aa", "*") → true
isMatch("aa", "a*") → true
isMatch("ab", "?*") → true
isMatch("aab", "c*a*b") → false
def is_match(s: str, p: str):
"""
带备忘录的递归解法
:param s:
:param p:
:return:
"""
memo = {} # 字典类型, 建立备忘录, 避免递归中的重复计算
ssize, psize = len(s), len(p)
def help_match(i, j):
print(memo)
if (i, j) in memo:
return memo[(i, j)]
# 递归基1
if j == psize: # 当 j 到尾部时,若文本串s也到尾部,则说明恰好匹配
memo[(i, j)] = i == ssize
return memo[(i, j)]
# 递归基2
if i == ssize:
# 当 i==ssize,即文本串 s 到尾部,且 j 不可能为 psize (若是则会进入前一个条件语句);
# 此时若模式串剩余部分全为"*"则可匹配,否则不匹配。
if p[j] == '*':
memo[(i, j)] = help_match(i, j + 1)
else:
memo[(i, j)] = False
return memo[(i, j)]
if s[i] == p[j] or p[j] == "?": # 该行是help_match(0, 0)正式执行的第一句
memo[(i, j)] = help_match(i + 1, j + 1)
elif p[j] == "*": # 如果字串这时的位置再一个 *
# "*" 匹配空字符串或一个字符(由于递归,可以代表匹配多个字符的字符串)
memo[(i, j)] = help_match(i + 1, j) or help_match(i, j + 1)
else:
memo[(i, j)] = False
return memo[(i, j)]
return help_match(0, 0)
if __name__ == '__main__':
s1 = "aaba"
s2 = "*?a"
print(is_match(s1, s2))
{}
{}
{}
{}
{}
{}
{(4, 1): False, (4, 0): False}
{(4, 1): False, (4, 0): False}
{(4, 1): False, (4, 0): False, (4, 2): False, (3, 1): False, (3, 0): False}
{(4, 1): False, (4, 0): False, (4, 2): False, (3, 1): False, (3, 0): False}
{(4, 1): False, (4, 0): False, (4, 2): False, (3, 1): False, (3, 0): False}
True
def isMatch(s, p):
m, n = len(s), len(p)
# 状态数组,表示从 s 的 i 到 p 的 j 是否可以匹配,初始化,m+1 行 n+1 列 False
dp = [[False for j in range(n + 1)] for i in range(m + 1)]
dp[0][0] = True
# 当 s 为空时必须有 * 才可能满足匹配,并且他的真值一定和去掉 * 及其前面的符号相同
for j in range(1, n + 1): # 1. 更新第START行的START列,即dp[0]行
dp[0][j] = p[j - 1] == '*' and dp[0][j - 1]
for i in range(1, m + 1): # 2. 更新后面几行,s字符串正式与通配符字符串开始匹配,
for j in range(1, n + 1):
# 当 p上一个字符为'?'或者p上一个字符等于s上一个字符,则当前真值与上一位相同
if p[j - 1] == s[i - 1] or p[j - 1] == '?':
dp[i][j] = dp[i - 1][j - 1]
# p上一个字符为'*'时,则*可表示p往后走一位或者s往后走一位
elif p[j - 1] == '*':
dp[i][j] = dp[i - 1][j] or dp[i][j - 1]
# 返回s[0->len(s)] 与 p[0->len(p)]的真值
print(dp)
return dp[m][n]
if __name__ == '__main__':
s1 = "aaba"
s2 = "*ba"
print(isMatch(s1, s2))
- 对于 *,它的值从上一行的值和 d p [ i − 1 ] [ j − 1 ] dp[i-1][j-1] dp[i−1][j−1] 来确定,有一个真,就为: T u r e Ture Ture
- 对于 ?,它的值从 d p [ i − 1 ] [ j − 1 ] dp[i-1][j-1] dp[i−1][j−1] 变到 d p [ i ] [ j ] dp[i][j] dp[i][j]
- 注意: d p dp dp数组的0行,0列属于过渡,没有字符串匹配。