字符串的常见操作
基本操作以及正则表达式的表达。
给定两个字符串 s1 和 s2,要求判定 s2 是否能够被 s1 做循环移位得到的字符串包含。
s1 进行循环移位的结果是 s1s1 的子字符串,因此只要判断 s2 是否是 s1s1 的子字符串即可。
s1 = AABCD, s2 = CDAA
Return : true
判断是否包含:indexOf(String s)
将字符串向右循环移动 k 位。
将 abcd123 中的 abcd 和 123 单独翻转,得到 dcba321,然后对整个字符串进行翻转,得到 123abcd。
字符串的子串判定,翻转。
将每个单词翻转,然后将整个字符串翻转。
可以用 HashMap 来映射字符与出现次数,然后比较两个字符串出现的字符数量是否相同。
由于本题的字符串只包含 26 个小写字符,因此可以使用长度为 26 的整型数组对字符串出现的字符进行统计,不再使用 HashMap。
注意字符与数组下标之间的转换方法。
public boolean isAnagram(String s, String t) {
int[] cnts = new int[26];
for (char c : s.toCharArray()) {
cnts[c - 'a']++;
}
for (char c : t.toCharArray()) {
cnts[c - 'a']--;
}
for (int cnt : cnts) {
if (cnt != 0) {
return false;
}
}
return true;
}
简化方法进行
public boolean isAnagram(String s, String t) {
int[] cnts = new int[26];
for (char c : s.toCharArray()) {
cnts[c - 'a']++;
}
for (char c : t.toCharArray()) {
cnts[c - 'a']--;
if( cnts[c - 'a']<0){
return false;
}
return true;
}
简化方法进行
class Solution {
public boolean isAnagram(String s, String t) {
int[] cnts = new int[26];
if(s.length()!=t.length()) return false;
for (char c : s.toCharArray()) {
cnts[c - 'a']++;
}
for (char c : t.toCharArray()) {
cnts[c - 'a']--;
if( cnts[c - 'a']<0){
return false;
}
}
return true;
}
}
为什么使用这个长度的数组我不是很清楚,128个即可。使用长度为 256 的整型数组来统计每个字符出现的个数,每个字符有偶数个可以用来构成回文字符串。因为回文字符串最中间的那个字符可以单独出现,所以如果有单独的字符就把它放到最中间。
为奇数个的字符也是可以取偶数个先进行个数的计算,之后随便选一个单字符放到中间即可。
Array继承自Object,所以可以字符或者字符串作为数组下标。
public int longestPalindrome(String s) {
int[] cnts = new int[256];
for (char c : s.toCharArray()) {
cnts[c]++;
}
int palindrome = 0;
for (int cnt : cnts) {
palindrome += (cnt / 2) * 2;
}
if (palindrome < s.length()) {
palindrome++; // 这个条件下 s 中一定有单个未使用的字符存在,可以把这个字符放到回文的最中间
}
return palindrome;
}
记录一个字符上次出现的位置,如果两个字符串中的字符上次出现的位置一样,那么就属于同构。这个问题简化,就是每次字符出现的位置要相同,及对应的字符顺序相同。
public boolean isIsomorphic(String s, String t) {
int[] preIndexOfS = new int[256];
int[] preIndexOfT = new int[256];
for (int i = 0; i < s.length(); i++) {
char sc = s.charAt(i), tc = t.charAt(i);
if (preIndexOfS[sc] != preIndexOfT[tc]) {
return false;
}
preIndexOfS[sc] = i + 1;
preIndexOfT[tc] = i + 1;
}
return true;
}
动态规划方法,属于暴力搜索
dp[i][j]表示char[j]-char[i]之间的为回文字符串
状态转移方程
dp[i][j]= str[i]=str[j]&&dp[i+1][j-1]注意对特殊位置的处理
public int countSubstrings(String s) {
int res = 0;
boolean dp[][] = new boolean[s.length()][s.length()];
for (int j = 0; j < s.length(); j++) {
for (int i = j; i >= 0; i--) {
if (s.charAt(i) == s.charAt(j) && ((j - i < 2) || dp[i + 1][j - 1])) {
dp[i][j] = true;
res++;
}
}
}
return res;
}
中心外侧延申法
private int cnt = 0;
public int countSubstrings(String s) {
for (int i = 0; i < s.length(); i++) {
extendSubstrings(s, i, i); // 奇数长度
extendSubstrings(s, i, i + 1); // 偶数长度
}
return cnt;
}
private void extendSubstrings(String s, int start, int end) {
while (start >= 0 && end < s.length() && s.charAt(start) == s.charAt(end)) {
start--;
end++;
cnt++;
}
}
字符串法
public boolean isPalindrome(int x) {
if(x==0){
return true;
}
if (x < 0 || x % 10 == 0) {
return false;
}
String sp= String.valueOf(x);
StringBuffer mp= new StringBuffer(sp);
String tp=mp.reverse().toString();
return sp.equals(tp);
}
整数直接判断,用数学方法将字符反转
public boolean isPalindrome(int x) {
if (x == 0) {
return true;
}
if (x < 0 || x % 10 == 0) {
return false;
}
int right = 0;
while (x > right) {
right = right * 10 + x % 10;
x /= 10;
}
return x == right || x == right / 10;
}
扩张法求解
public int countBinarySubstrings(String s) {
int ans=0;
char[] num =s.toCharArray();
for(int i=0;i<s.length()-1;i++){//从前往后遍历
if(num[i]!=num[i+1]){//发现0,1相邻,开始向两边扩张
int left=i;
int right=i+1;
while(left>0&&right<s.length()-1){//当扩张到下一个0,1相邻和边界时跳出
if((num[left]!=num[left-1])||(num[right]!=num[right+1])){
break;
}
left--;
right++;
}
ans+=right-i;//此时符合条件的数量为长度的一半
i=right-1;//直接跳到下一个
}
}
return ans;
}
部分思路参考整理:https://github.com/CyC2018/CS-Notes/blob/master/notes/Leetcode%20%E9%A2%98%E8%A7%A3%20-%20%E5%AD%97%E7%AC%A6%E4%B8%B2.md#6-%E5%AD%97%E7%AC%A6%E4%B8%B2%E5%90%8C%E6%9E%84