字符串相关内容

字符串

基础知识

	字符串由任意长度(长度可能为0)的字符组成,是编程语言中表示文本的数据类型。Java中用定义的类型
String来表示字符串。

常用函数

序号函数函数功能
1charAt返回指定下标处的字符
2compareTo按照字典顺序比较两个字符串
3equals判断两个字符串的长度和内容是否相同
4indexOf返回字符串中某个字符或字符串首次出现的下标位置
5lastIndexOf返回字符串中某个字符或子字符串最后出现的下标位置
6length返回字符串长度
7split将字符串按照指定的分隔符进行分割
8substring根据下标截取字符串
9toLowerCase/toUpperCase将字符中所有的字母改成小写或大写

双指针

	前一节说的双指针用于数组,那么同样的字符串也可以转成数组来使用双指针。

例题解答

1、字符串中的变位词

题目
	输入字符串s1和s2,如何判断字符串s2中是否包含字符串s1的某个变位词?如果字符串s2中包含字符串s1的
某个变位词,则字符串s1至少有一个变位词是字符串s2的子字符串。假设两个字符串中只包含英文小写字母。例
如,字符串s1为"ac",字符串s2为"dgcaf",由于字符串s2中包含字符串s1的变位词"ca",因此输出为true.如
果字符串s1为"ab",字符串s2为"dgcaf",则输出为false。
分析
	变位词实际上是字符串的所有排列,可以使用暴力解法,先获取s1的所有变位词,然后再遍历是否在s1中
被包含。
	还可以再进行优化使用双指针和哈希表,变位词与字母及字母出现的次数有关,那么统计字符串中包含字母
及每个字母出现的次数,存入哈希表中。因为题目中字母都是英文小写,共26个,所以可以使用一个数组模拟,
数组下标0对应字母'a',它的值对应字母'a'出现的次数,数组下标1对应字母'b',它的值对应字母'b'出现
的次数,一次类推。
	根据上面分析后,先扫描s1,每扫描到一个字符,就找到它在哈希表中的位置,并把它对应的值加1。然后
考虑s2是否包含字符串s1的变位词,每次移动需要判断s1长度的s2子字符串是否是s1的变位词。代码如下:
示例代码
public static boolean checkInclusion(String s1,String s2){
	if(s1.length() > s2.length()){
		return false;
	}
	int[] counts = new int[26];
	for(int i = 0; i < s1.length(); i ++){
		counts[s1.charAt(i) - 'a'] ++;
		counts[s2.charAt(i) - 'a'] --;
	}
	if(areAllZero(counts)){
		return true;
	}
	//下面移动相当于字符串右边增加一位,左边减少一位
	for(int i = s1.length(); i < s2.length(); i ++){
		counts[s2.charAt(i) - 'a'] --;
		counts[s2.charAt(i - s1.length()) - 'a'] ++;
		if(areAllZero(counts)){
			return true;
		}
	}
	return false;
}

//判断counts是否都为0,如果不是,则不是该变位词不是子字符串
private static boolean areAllZero(int[] counts){
	for(int count : counts){
		if(count != 0){
			return false;
		}
	}
	return true;
}

2、字符串中的所有变位词

题目
	输入字符串s1和s2,如何找出字符串s2的所有变位词在字符串s1中的起始下标?假设两个字符串中只包含英文小写字母,例如,字符串s1为
"cbadabacg",字符串s2为"abc",字符串s2的两个变位词"cba","bac"是字符串s1中的子字符串,输出它们在字符串s1的起始下标0和5。
分析
	和上题类似,可以用双指针和哈希表解决
示例代码
public static List<Integer> findAnagrams(String s1,String s2){
	List<Integer> result = new ArrayList<>();
	int s1Length = s1.length();
	int s2Length = s2.length();
	if(s1Length < s2Length){
		return result;
	}
	int[] counts = new int[26];
	for(int i = 0; i < s2Length; i ++){
		counts[s2.charAt(i) - 'a'] ++;
		counts[s1.charAt(i) - 'a'] --;
	}
	if(areAllZero(counts)){
		result.add(0);
	}
	for(int i = s2Length; i < s1Length; i ++){
		counts[s1.charAt(i) - 'a'] --;
		counts[s1.charAt(i - s2Length) - 'a'] ++;
		if(areAllZero(counts)){
			result.add(i - s2Length + 1);
		}
	}
	return result;
}
private static boolean areAllZero(int[] counts){
	for(int count : counts){
		if(count != 0){
			return false;
		}
	}
	return true;
}

3、不含重复字符的最长子字符串

题目
	输入一个字符串,求该字符串中不含重复字符的最长子字符串的长度。例如,输入字符串"babcca",其最长的不含重复字符的子字符串是"abc",
长度为3。
分析
	可以使用一个哈希表存储字符出现的次数,使用双指针,每次向后移动快指针则字符串增加一位,当有字符出现大于1次时,向后移动慢指针字符串减少一位,每次赋值给结果快指针与满指针的差值最大值即可获得结果。
示例代码
public static int lengthOfLongestSubstring(String s){
	int result = 0;
	int length = s.length();
	if(length == 0){
		return result;
	}
	int[] counts = new int[256];
	int countTemp = 0;
	int low = -1;
	for(int i = 0; i < length; i ++){
		counts[s.charAt(i)]++;
		if(counts[s.charAt(i)] == 2){
			countTemp ++;
		}
		while(countTemp > 0){
			low ++;
			counts[s.charAt(low)] --;
			if(counts[s.charAt(low)] == 1){
				countTemp --;
			}
		}
		result = Math.max(i - low, result);
	}
	return result;
}

4、包含所有字符的最短字符串

题目
	输入两个字符串s和t,请找出字符串s中包含t的所有字符的最短字符串。例如,输入的字符串s为"ADDBANCAD",字符串t为"ABC",则字符串s中包含
字符'A'、'B'、’C'的最短子字符串是"BANC"。如果不存在符合条件的子字符串,则返回空字符串""。如果存在多个符合条件的子字符串,则返回任意一个。
分析
	一个字符串s中包含另一个字符串t的所有字符,那么字符串t的所有字符在字符串s中都出现,并且同一个字符在字符串s中出现的次数不少于在字符串t
中出现的次数。由此可以使用哈希表存储字符和字符次数来解决,先扫描字符串t中字符并存入到哈希表中,然后扫描s字符串,使用双指针方式进行,
一个指针指向s的第一个字符,一个指针指向s的最后一个字符,然后判断进行指针移动计算结果。
示例代码
public static String minWindow(String s,String t){
	// 存储t字符串中字符与字符数量
	Map<Character,Integer> charToCount = new HashMap<>();
	for(char ch : t.toCharArray()){
		charToCount.put(ch, charToCount.getOrDefault(ch,0) + 1);
	}
	// t字符串中字符总数
	int count = charToCount.size();
	//声明指针,由于要获取到最小字符串,所以增加minStart和minEnd
	int start = 0, end = 0, minStart = 0, minEnd = 0;
	//由于要最小字符串,所以这里使用最大整数值,后续进行比较替换
	int minLength = Integer.MAX_VALUE;
	int sLength = s.length();
	// end指针没有超过字符串长度时,一直进行循环;或者t字符已经为空,end指针到终点时,也需要循环,移动start指针。
	while(end < sLength || (count == 0 && end == sLength)){
		if(count > 0){
		//如果t字符的统计总数不为空时,移动end指针,说明start到end指针的子字符串还没完全包含t字符串
			char endCh = s.charAt(end);
			if(charToCount.containsKey(endCh)){
				charToCount.put(endCh,charToCount.get(endCh) - 1);
				if(charToCount.get(endCh) == 0){
					count --;
				}
			}
			end ++;
		}else{
			// 说明start到end指针之间子字符串已经全部包含了t字符串
			if(end - start < minLength){
				minLength = end - start;
				minStart = start;
				minEnd = end;
			}
			char startCh = s.charAt(start);
			//count 小于等于0,不代表charToCount等于空,start指针往右移动需要判断字符的数量是否大于等于1
			if(charToCount.containsKey(startCh)){
				charToCount.put(startCh,charToCount.get(startCh) + 1);
				if(charToCount.get(startCh) >= 1){
					count ++;
				}
			}
			start ++;
		}
	}
	return minLength < Integer.MAX_VALUE ? s.substring(minStart,minEnd) : "";
}

回文字符串

	回文字符串是一类特殊的字符串,不管是从头到尾读还是从尾到头读,读取的字符串结果都一样。

5、有效的回文

题目
	给定一个字符串,请判断它是不是回文。假设只需要考虑字母和数字字符,并忽略大小写。例如,"Was it a cat I saw?"是一个回文字符串,而
"race a car"不是一个回文字符串。
分析
	判断一个字符串是不是回文,常用的方法是双指针相反方向移动。直到两个指针相遇,或有不相同的字符时终止。
示例代码
    public static boolean isPalindrome(String s){
        int i = 0;
        int j = s.length() - 1;
        while(i < j){
            char ch1 = s.charAt(i);
            char ch2 = s.charAt(j);
            if(!Character.isLetterOrDigit(ch1)){
                i ++;
            }else if(!Character.isLetterOrDigit(ch2)){
                j --;
            }else{
                ch1 = Character.toLowerCase(ch1);
                ch2 = Character.toLowerCase(ch2);
                if(ch1 != ch2){
                    return false;
                }
            }
            i ++;
            j --;
        }
        return true;
    }

6、最多删除一个字符得到回文

题目
	给定一个字符串,请判断如果最多从字符串中删除一个字符能不能得到一个回文字符串。例如,如果输入字符串"abca",由于删除字符'b'或者字符'c'
就能得到一个回文字符串,因此输出为true。
分析
	由于事先不知道删除哪个字符,则可以两边字符都进行尝试,解题思路和上题类似。
示例代码
public static boolean validPalindrome(String s){
int start = 0;
int end = s.length() - 1;
for(; start < s.length()/2; start ++, end --){
	//如果出现不是回文字符会终止循环,star和end会是终止位置的值
	if(s.charAt(start) != s.charAt(end)){
		break;
	}
}
//如果start能移动到字符长度的中间则 说明是回文,如果移动不到,则删除最后一个或者当前的一个继续判断
return start == s.length() /2 || isPalindrome(s,start,end - 1) || isPalindrome(s,start + 1,end);
}
private static boolean isPalindrome(String s, int start ,int end){
	while(start < end){
		if(s.charAt(start) != s.charAt(end)){
			break;
		}
		start ++;
		end --;
	}
	//如果start能和end相等,则说明是回文
	return start >= end;
}

7、回文字符串的个数

题目
	给定一个字符串,请问该字符串中有多少回文连续子字符串?例如,字符串"abc"有3个回文子字符串,分别为"a"、"b"、"c";而字符串"aaa"有6
个回文子字符串,分别为"a","a","a","aa","aa"和"aaa"。
分析
	可以从字符串中间向两边移动指针来解决,从中间位置开始,往左一个,往右一个,如果相同则,会获得一个回文字符串。
示例代码
public static int countSubstrings(String s){
	if(s == null ||s.length() == 0){
		return 0;
	}
	int result = 0;
	for(int i = 0; i < s.length(); i ++){
		result += countPalindrome(s,i,i);
		result += countPalindrome(s,i,i + 1);
	}
	return result;
}
//获取每个字符串,从起始到结束的回文字符串个数
private static int countPalindrome(String s,int start,int end){
	int count = 0;
	while(start >= 0 && end < s.length() && s.charAt(start) == s.charAt(end)){
		count ++;
		start --;
		end ++;
	}
	return count;
}

总结

	熟练使用字符串的函数,双指针思想,字符的性质,能很好解决一些问题
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

听风当歌

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值