文章目录
1.正则表达式匹配
题目
答案
当模式中的第二个字符不是“ * ”时:
1、如果字符串第一个字符和模式中的第一个字符相匹配,
那么字符串和模式都后移一个字符,然后匹配剩余的。
2、如果字符串第一个字符和模式中的第一个字符相不匹配,直接返回false。
而当模式中的第二个字符是“ * ”时:
如果字符串第一个字符跟模式第一个字符不匹配,则模式后移2个字符,继续匹配。
如果字符串第一个字符跟模式第一个字符匹配,可以有3种匹配方式:
1、模式后移2字符,相当于x * 被忽略;
2、字符串后移1字符,模式后移2字符; 相当于x * 算一次
3、字符串后移1字符,模式不变,即继续匹配字符下一位,因为 * 可以匹配多位,相当于算多次
这里需要注意的是:Java里,要时刻检验数组是否越界。
代码如下:
public boolean match(char[] str, char[] pattern){
int strStart = 0;
int patternStart = 0;
return matchPox(str, strStart, pattern, patternStart);
}
public boolean matchPox(char[] str, int strStart, char[] pattern, int patternStart) {
// 有效检验:是否同时到尾
if(str.length == strStart && pattern.length <= patternStart)
return true;
// 有效检验:str没有到尾 pattern已经到尾
if(strStart < str.length && patternStart == pattern.length)
return false;
// 判断pattern的第二个字符是否为 *
if(patternStart + 1 < pattern.length && '*' == pattern[patternStart + 1]) {
// 如果是,在判断str和pattern的第一个是否相等,
if((strStart != str.length && str[strStart] == (pattern[patternStart]))
|| (strStart != str.length && '.' == pattern[patternStart]) ) {
// 如果相等,有三种情况:0, 1 ,多
// pattern滑动二个,str不变,*代表零个
// str和滑动一个,pattern滑动两个,*代表一个
// str滑动一个,pattern不变,*代表多个
return matchPox(str, strStart, pattern, patternStart + 2)
|| matchPox(str, strStart + 1, pattern, patternStart + 1)
|| matchPox(str, strStart + 1, pattern, patternStart);
}else {
// 如果不等,pattern滑动两个
return matchPox(str, strStart, pattern, patternStart + 2);
//
}
}else {
// 如果不是:
if((strStart != str.length && str[strStart] == (pattern[patternStart]))
|| (strStart != str.length && '.' == pattern[patternStart]) ) {
// str的第一个字符和pattern的第一个字符比较,如果相等,str与pattern各滑一
return matchPox(str, strStart + 1, pattern, patternStart + 1);
}else {
// 如果不相等,返回false
return false;
}
}
}
2.第一个只出现一次的字符
题目
在一个字符串(0<=字符串长度<=10000,全部由字母组成)中找到第一个只出现一次的字符,并返回它的位置, 如果没有则返回 -1(需要区分大小写)
答案
思路:采用空间换时间的方法。
将str
依次从0
到len-1
,放入到map
中,如果map中有这个key
就让value++
,如果没有,就让value = 1
,最后再遍历一次map
,value=1
的第一个元素就是第一个只出现一次的字符。
/**
* 第一个只出现一次的字符
* @param str
* @return
*/
public static int FirstNotRepeatingChar(String str) {
// 这里用LinkedHashMap,因为它是有序的,放进去的位置不会发生变化
LinkedHashMap<Character, Integer> map = new LinkedHashMap<Character, Integer>();
for(int i = 0; i < str.length(); i++) {
if(!map.containsKey(str.charAt(i)))
map.put(str.charAt(i), 1);
else
map.put(str.charAt(i), map.get(str.charAt(i)) + 1);
}
for(int i = 0; i < str.length(); i++) {
if(map.get(str.charAt(i)) == 1)
return i;
}
return -1;
}
3.翻转单词顺序列
题目
牛客最近来了一个新员工Fish,每天早晨总是会拿着一本英文杂志,写些句子在本子上。同事Cat对Fish写的内容颇感兴趣,有一天他向Fish借来翻看,但却读不懂它的意思。例如,“student. a am I”。后来才意识到,这家伙原来把句子单词的顺序翻转了,正确的句子应该是“I am a student.”。Cat对一一的翻转这些单词顺序可不在行,你能帮助他么?
答案
思路:
第一种:先将字符串用" "
分割成字符串数组,最后再用StringBuffer从数组的最后一个元素开始连接,除了最后一次连接外,每次连接都加一个“空格”。
第二种:先将整个字符串反转,然后再将每个单词反转,这个解题方法没有第一个好用,但是这是一种思路,在这道题里面可能没有第一种好用,但是在其他的题里面就不好说了。
/**
* 翻转单词顺序列
* @param str
* @return
*/
//第一种思路
public static String ReverseSentence(String str) {
if(str == null || str.length() == 0)
return str;
if (str.trim().length() == 0)
return str;
String[] strA = str.split(" ");
StringBuffer sb = new StringBuffer();
for(int i = strA.length - 1; i >= 0; i--) {
sb.append(strA[i]);
if(i > 0)
sb.append(" ");
}
return sb.toString();
}
//第二种思路:
public String ReverseSentence(String str) {
if (str == null || str.length() == 0)
return str;
if (str.trim().length() == 0)
return str;
StringBuilder sb = new StringBuilder();
String re = reverse(str);
String[] s = re.split(" ");
for (int i = 0; i < s.length - 1; i++) {
sb.append(reverse(s[i]) + " ");
}
sb.append(reverse(s[s.length - 1]));
return String.valueOf(sb);
}
public String reverse(String str) {
StringBuilder sb = new StringBuilder();
for (int i = str.length() - 1; i >= 0; i--) {
sb.append(str.charAt(i));
}
return String.valueOf(sb);
}
4.左旋转转字符串
题目
汇编语言中有一种移位指令叫做循环左移(ROL),现在有个简单的任务,就是用字符串模拟这个指令的运算结果。对于一个给定的字符序列S,请你把其循环左移K位后的序列输出。例如,字符序列S=”abcXYZdef”,要求输出循环左移3位后的结果,即“XYZdefabc”。是不是很简单?OK,搞定它!
答案
思路:
第一种:前n位反转,后几位反转,最后总的反转
先反转前n位,再反转后几位,变为了cbafedZYX,再整体反转变为XYZdefabc
第二种:就很简单,只要取出字符串的前n位子串和后面的子串,只要把前n位的子串接到后面子串的尾部,就OK。
/**
* 左旋转转字符串
* @param str
* @param n
* @return
*/
//第一种:
public static String LeftRotateString(String str,int n) {
if(str == null || str.length() == 0)
return str;
char[] charStr = str.toCharArray();
int len = charStr.length;
n = n % len;
reverse(charStr, 0, n - 1);
reverse(charStr, n, len - 1);
reverse(charStr, 0, len - 1);
return new String(charStr);
}
public static void reverse(char[] charStr, int i, int j) {
while(i < j) {
char temp = charStr[i];
charStr[i] = charStr[j];
charStr[j] = temp;
i++;
j--;
}
}
//第二种:
public static String LeftRotateString(String str, int n) {
if (str == null || str.length() == 0)
return str;
n = n % str.length();
if (n == 0)
return str;
String str1 = str.substring(0, n);
String str2 = str.substring(n, str.length());
return new StringBuffer(str2).append(str1).toString();
}
5.把字符串转换成整数
题目
题目描述:
将一个字符串转换成一个整数(实现Integer.valueOf(string)的功能,但是string不符合数字要求时返回0),要求不能使用字符串转换整数的库函数。 数值为0或者字符串不是一个合法的数值则返回0。
输入描述:
输入一个字符串,包括数字字母符号,可以为空
输出描述:
如果是合法的数值表达则返回该数字,否则返回0
示例1
输入:
+2147483647
1a33
输出:
2147483647
0
答案
思路:这个简单,主要就是检测判断,只要能做出来,性能都差不多一样,就是写得好的算法看上去简洁,好理解。
/**
* 字符串转整数
* @param str
* @return
*/
public static int strToInt(String str) {
int len = str.length(), sign = 1, res = 0;
if(len == 0 || str == null)
return 0;
if(str.charAt(0) == '-')
sign = -1;
for(int i = (str.charAt(0) == '+' || str.charAt(0) == '-') ? 1 : 0; i < len; i++) {
if(str.charAt(i) < '0' || str.charAt(i) > '9')
return 0;
res = res * 10 + str.charAt(i) - '0';
}
return sign*res;
}
6.*字符的全排列
题目
输入一个字符串,打印出该字符串中字符的所有排列。
例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。
答案
思路:
固定第一个字符,递归取得首位后面的各种字符串组合;
再把第一个字符与后面每一个字符交换,并同样递归获得首位后面的字符串组合;
递归的出口,就是只剩一个字符的时候,递归的循环过程,就是从每个子串的第二个字符开始依次与第一个字符交换,然后继续处理子串。
假如有重复值呢?
第一种方法:
由于全排列就是从第一个数字起,每个数分别与它后面的数字交换,我们先尝试加个这样的判断——如果一个数与后面的数字相同那么这两个数就不交换了。
例如abb
,第一个数与后面两个数交换得bab
,bba
。然后abb
中第二个数和第三个数相同,就不用交换了。
但是对bab
,第二个数和第三个数不 同,则需要交换,得到bba
。
由于这里的bba
和开始第一个数与第三个数交换的结果相同了,因此这个方法不行。
换种思维,对abb
,第一个数a与第二个数b交换得到bab
,然后考虑第一个数与第三个数交换,此时由于第三个数等于第二个数。所以第一个数就不再用与第三个数交换了。
再考虑bab
,它的第二个数与第三个数交换可以解决bba
。此时全排列生成完毕!
第二中方法:
比较笨,把所有的结果放在list中,在每次放的时候,都检查一下list中是否存在这个串,如果不存在,就放进去。
第一种方法相对于第二种, 虽然只是看上去只是很微小的改变,但是时间上面的改进不只是十几倍,而是几十倍上百倍。
/**
* 字符全排列
* @param str
* @return
*/
public static ArrayList<String> Permutation(String str) {
List<String> res = new ArrayList<String>();
if(str != null && str.length() > 0)
PermutationHelp(str.toCharArray(), 0, res);
Collections.sort(res);
return (ArrayList<String>)res;
}
public static void PermutationHelp(char[] ch, int index, List<String> res) {
if(index == ch.length -1) {
String str = String.valueOf(ch);
// if(!res.contains(str))
res.add(str);
}else {
for(int i = index; i < ch.length; i++) {
/*
* 理解了这个点,也就对这个算法的核心思想理解了
* 去掉下面的if检测,去掉上面的if(!res.contains(str))注释,就是第二种方法
*/
if(isSwap(ch, index, i)) {
swap(ch, index, i);
PermutationHelp(ch, index + 1, res);
swap(ch, index, i);
}
}
}
}
public static void swap(char[] ch, int i, int j) {
char temp = ch[i];
ch[i] = ch[j];
ch[j] = temp;
}
public static boolean isSwap(char[] ch, int begin, int end) {
boolean sign = true;
for(int i = begin; i < end; i++) {
if(ch[i] == ch[end])
sign = false;
}
return sign;
}
也可以用hashset来取出重复的,简单高效!
/**
* 字符全排列
*
* @param str
* @return
*/
public static ArrayList<String> Permutation(String str) {
List<String> res = new ArrayList<String>();
HashSet<String> set = new HashSet<>();
if (str != null && str.length() > 0)
PermutationHelp(str.toCharArray(), 0, set);
res.addAll(set);
Collections.sort(res);
return (ArrayList<String>) res;
}
public static void PermutationHelp(char[] ch, int index, HashSet<String> set) {
if (index == ch.length - 1) {
String str = String.valueOf(ch);
set.add(str);
} else {
for (int i = index; i < ch.length; i++) {
swap(ch, index, i);
PermutationHelp(ch, index + 1, set);
swap(ch, index, i);
}
}
}
public static void swap(char[] ch, int i, int j) {
char temp = ch[i];
ch[i] = ch[j];
ch[j] = temp;
}
7.表示数值的字符串
题目
答案
思路:
按照一定的规则,如果第一位是+或-,就后移一位。
如果是数字,索引后移,数字表示1.
如果是点,要判断至此点的数量和e的数量是否已经有了,因为java 中e要求后面为整数,如果有了肯定false。索引后移,dotnum增加。
如果是e,判断是否重复e,或者前面没有数字返回false。enum++, 索引++,此时还要判断最后一位是不是e或者+或者-,如果是false。
/**
* 表示数值的字符串
* @param str
* @return
*/
public static boolean isNumeric(char[] str) {
if (str == null)
return false;
int length = str.length;
int dotNum = 0;// 记录点的数量
int index = 0;// 索引
int eNum = 0;// 记录e的数量
int num = 0;// 记录数字的数量
if (str[0] == '+' || str[0] == '-') {
index++;
}
while (index < length) {
if (str[index] >= '0' && str[index] <= '9') {
index++;
num = 1;
// .前面可以没有数字,所以不需要判断num是否为0
} else if (str[index] == '.') {
// e后面不能有.,e的个数不能大于1.java科学计数要求aeb,b为整数
if (dotNum > 0 || eNum > 0)
return false;
dotNum++;
index++;
} else if (str[index] == 'e' || str[index] == 'E') {
// 重复e或者e前面没有数字
if (eNum > 0 || num == 0)
return false;
eNum++;
index++;
// 符号不能在最后一位
if (index < length && (str[index] == '+' || str[index] == '-'))
index++;
// 表示e或者符号在最后一位
if (index == length)
return false;
} else {
return false;
}
}
return true;
}
8.字符流中第一个不重复的字符
题目
答案
思路:很简单,两种方法,hashmap和 字符数组。
hashmap:
/**
* 字符流中第一个不重复的字符
*/
HashMap<Character, Integer> map = new HashMap<>();// 记录字符出现次数
ArrayList<Character> list = new ArrayList<>();// 记录当前的所有的字符
// Insert one char from stringstream
public void Insert(char ch) {
if (map.containsKey(ch))
map.put(ch, map.get(ch) + 1);
else
map.put(ch, 1);
list.add(ch);
}
// return the first appearence once char in current stringstream
public char FirstAppearingOnce() {
for (char c : list) {
if (map.get(c) == 1)
return c;
}
return '#';
}
字符数组:
char [] chars = new char[256];//ascii字符共128,其他字符非中文认为256个,
//为每个字符预留空间。默认每个存的ascii值为0
StringBuffer sb = new StringBuffer();//记录当前的所有字符
//Insert one char from stringstream
public void Insert(char ch)
{
sb.append(ch);
chars[ch]++;//如果字符是1,那么就是在字符1对应的下标的地方
//也就是49的下标处,ascii加1.此时如果输出chars[ch],里面存ascii值
//为1,所以是一个不可显示的字符。
}
//return the first appearence once char in current stringstream
public char FirstAppearingOnce()
{
char [] str = sb.toString().toCharArray();
for(char c:str) {
if(chars[c] == 1)//判断这个字符数组中在这个字符下标处值是否为1.
return c;
}
return '#';
}