3. Longest Substring Without Repeating Characters
Given a string, find the length of the longest substring without repeating characters.
思路:HashMap存字符和位置,两个指针i, j分别指向不重复字符串的首和尾,如果j指向的字符重复出现了,则更新i。在每一步都更新max和hashmap。注意如果hashmap中存的j指向字符的位置是在i之前的,那么是不算重复的。
public class Solution {
public int lengthOfLongestSubstring(String s) {
if(s.length() == 0) return 0;
Map<Character, Integer> map = new HashMap<>();
int i = 0, j = 0, max = 0;
while(j < s.length()) {
if(map.containsKey(s.charAt(j)) && map.get(s.charAt(j)) >= i){
i = map.get(s.charAt(j)) + 1;
}
max = Math.max(max, j - i + 1);
map.put(s.charAt(j), j);
j++;
}
return max;
}
}
5. Longest Palindromic Substring
Given a string s, find the longest palindromic substring in s. You may assume that the maximum length of s is 1000.
思路:从中间往两边扩充的思路,分成奇偶两种情况。
public class Solution {
int start, maxLen = Integer.MIN_VALUE;
public String longestPalindrome(String s) {
int n = s.length();
if(n < 2) return s;
for(int i=0; i<n-1; i++) {
extend(s, i, i);
extend(s, i, i+1);
}
return s.substring(start, start + maxLen);
}
private void extend(String s, int i, int j) {
while(i>=0 && j<s.length() && s.charAt(i) == s.charAt(j)) {
i--;
j++;
}
if(j - i - 1 > maxLen) {
start = i+1;
maxLen = j - i - 1;
}
}
}
8. String to Integer (atoi)
Implement atoi to convert a string to an integer.
Hint: Carefully consider all possible input cases. If you want a challenge, please do not see below and ask yourself what are the possible input cases.
Notes: It is intended for this problem to be specified vaguely (ie, no given input specs). You are responsible to gather all the input requirements up front.
思路:主要是要考虑各种情况。判断大数溢出的方法可以借鉴。
public class Solution {
public int myAtoi(String str) {
if(str == null || str.length() == 0) return 0;
final int MAX_DIV = Integer.MAX_VALUE / 10;
final int MIN_DIV = - (Integer.MIN_VALUE / 10);
final int MAX_MOD = Integer.MAX_VALUE % 10;
final int MIN_MOD = - (Integer.MIN_VALUE % 10);
int ret = 0;
char c = str.charAt(0);
int i = 0;
while(c == ' ') {
c = str.charAt(++i);
}
boolean isMinus = false;
if(c == '+' || c == '-') {
if(str.length() == 1)
return 0;
if(c == '-') {
isMinus = true;
}
i++;
}
while(i < str.length()) {
int digit = str.charAt(i) - '0';
if(digit >= 0 && digit <=9) {
if(!isMinus && (ret > MAX_DIV || (ret == MAX_DIV && digit > MAX_MOD))) {
return Integer.MAX_VALUE;
}
if(isMinus && (ret > MIN_DIV || (ret == MIN_DIV && digit > MIN_MOD))) {
return Integer.MIN_VALUE;
}
ret = ret * 10 + digit;
}
else {
break;
}
i++;
}
return isMinus ? - ret : ret;
}
}
10. Regular Expression Matching
Implement regular expression matching with support for ‘.’ and ‘*’.
‘.’ Matches any single character.
‘*’ Matches zero or more of the preceding element.
The matching should cover the entire input string (not partial).
The function prototype should be:
bool isMatch(const char *s, const char *p)
Some examples:
isMatch(“aa”,”a”) → false
isMatch(“aa”,”aa”) → true
isMatch(“aaa”,”aa”) → false
isMatch(“aa”, “a*”) → true
isMatch(“aa”, “.*”) → true
isMatch(“ab”, “.*”) → true
isMatch(“aab”, “c*a*b”) → true
解法1:递归。主要是分两种情况分别处理,下一个字符是*号和下一个字符不是*号。
public class Solution {
public boolean isMatch(String s, String p) {
if(s == null || p == null) return false;
return matchCore(s.toCharArray(), p.toCharArray(), 0, 0);
}
private boolean matchCore(char[] str, char[] pattern, int strStart, int patternStart) {
if(strStart == str.length && patternStart == pattern.length) {
return true;
}
if(strStart != str.length && patternStart == pattern.length) {
return false;
}
// 由于str为空,pattern不为空的情况在前面没有考虑,所以下面可能会出现这种情况,需要对strStart的合法性做判断
//下一个匹配模式里有*号
if(patternStart < pattern.length-1 && pattern[patternStart+1] == '*') {
// 当前字符和模式里的字符匹配,要判断strStart下标的合法性
if(strStart < str.length &&
(str[strStart] == pattern[patternStart] || pattern[patternStart] == '.'))
return matchCore(str, pattern, strStart+1, patternStart) || // 匹配多个
matchCore(str, pattern, strStart+1, patternStart + 2) || // 匹配1个
matchCore(str, pattern, strStart, patternStart + 2); // 匹配0个
// 当前字符和模式里的字符不匹配,直接在模式里跳两个字符,相当于*号表示前一个字符出现0次
else
return matchCore(str, pattern, strStart, patternStart + 2);
}
//下一个匹配模式里无*号,且字符匹配,这里也要判断strStart下标的合法性
if(strStart < str.length &&
(str[strStart] == pattern[patternStart] || pattern[patternStart] == '.')) {
return matchCore(str, pattern, strStart+1, patternStart+1);
}
return false;
}
}
解法2:动态规划。
Here are some conditions to figure out, then the logic can be very straightforward.
1, If p.charAt(j) == s.charAt(i) : dp[i][j] = dp[i-1][j-1];
2, If p.charAt(j) == ‘.’ : dp[i][j] = dp[i-1][j-1];
3, If p.charAt(j) == ‘*’:
here are two sub conditions:
1 if p.charAt(j-1) != s.charAt(i) : dp[i][j] = dp[i][j-2] //in this case, a* only counts as empty
2 if p.charAt(j-1) == s.charAt(i) or p.charAt(j-1) == ‘.’:
dp[i][j] = dp[i-1][j] //in this case, a* counts as multiple a
or dp[i][j] = dp[i][j-1] // in this case, a* counts as single a
or dp[i][j] = dp[i][j-2] // in this case, a* counts as empty
public class Solution {
public boolean isMatch(String s, String p) {
boolean[][] dp = new boolean[s.length()+1][p.length()+1];
dp[0][0] = true;
for(int j=0; j<p.length(); j++) {
if(p.charAt(j) == '*' && dp[0][j-1]) {
dp[0][j+1] = true;
}
}
for(int i=0; i<s.length(); i++) {
for(int j=0; j<p.length(); j++) {
if(p.charAt(j) == '.' || p.charAt(j) == s.charAt(i)) {
dp[i+1][j+1] = dp[i][j];
}
else if(p.charAt(j) == '*') {
if(p.charAt(j-1) != s.charAt(i) && p.charAt(j-1) != '.')
dp[i+1][j+1] = dp[i+1][j-1];
else {
dp[i+1][j+1] = dp[i+1][j-1] || dp[i+1][j] || dp[i][j+1];
}
}
}
}
return dp[s.length()][p.length()];
}
}
14. Longest Common Prefix
Write a function to find the longest common prefix string amongst an array of strings.
思路:最长公共前缀肯定不会超过最短字符串的长度。所以先获得最短字符串的长度。然后一个二层循环,外层循环是最短字符串长度的每个位置,内层循环是每个字符串对应的这个位置的字符,然后比较所以字符串这个位置的字符是否相等,如果相同,将该字符加入结果中,否则返回结果。
public class Solution {
public String longestCommonPrefix(String[] strs) {
if(strs == null || strs.length == 0)
return "";
int minLen = strs[0].length();
for(int i=0;i<strs.length;i++) {
if(strs[i].length() < minLen) {
minLen = strs[i].length();
}
}
String result = "";
for(int i=0;i<minLen;i++) {
char c = strs[0].charAt(i);
for(int j=1;j<strs.length;j++) {
if(strs[j].charAt(i) != c) {
return result;
}
}
result = result + c;
}
return result;
}
}
20. Valid Parentheses
Given a string containing just the characters ‘(‘, ‘)’, ‘{‘, ‘}’, ‘[’ and ‘]’, determine if the input string is valid.
The brackets must close in the correct order, “()” and “()[]{}” are all valid but “(]” and “([)]” are not.
解题思路:这是一道典型的可以用栈来解决的问题。做一个空栈。一个个读入字符直到字符串结尾。如果字符是一个开放符号,则将其推入栈中。如果字符是一个封闭符号,则当栈空时报错。否则,将栈元素弹出。如果弹出的符号不是对应的开放符号,则报错。在字符串结尾,如果栈非空则报错。
public class Solution {
public boolean isValid(String s) {
Map<Character,Character> map = new HashMap<>();
map.put('(', ')');
map.put('[', ']');
map.put('{', '}');
Stack<Character> stack = new Stack<>();
for(int i=0;i<s.length();i++) {
char curr = s.charAt(i);
if(map.containsKey(curr)) {
stack.push(curr);
}
else {
if(!stack.empty() && map.get(stack.peek()) == curr) {
stack.pop();
}
else {
return false;
}
}
}
return stack.empty();
}
}
38. Count and Say
The count-and-say sequence is the sequence of integers beginning as follows:
1, 11, 21, 1211, 111221, …
1 is read off as “one 1” or 11.
11 is read off as “two 1s” or 21.
21 is read off as “one 2, then one 1” or 1211.
Given an integer n, generate the nth sequence.
思路:我表示这道题对我来说关键就是理解题意。。。真的没理解题意,网上查了才恍然大悟。首先说一下题意。n=1时输出字符串1;n=2时,数上次字符串中的数值个数,因为上次字符串有1个1,所以输出11;n=3时,由于上次字符是11,有2个1,所以输出21;n=4时,由于上次字符串是21,有1个2和1个1,所以输出1211。依次类推,写个countAndSay(n)函数返回字符串。理解题意后,关键就是如何say一串数字了。 依次读入每个字符,对于每个字符,首先判断用于计数的count是否为0,如果不为0,表示前面该字符前面已经有字符了,所以再判断该字符和前一个字符是否相等,如果不相等,就把前面计算的结果添加到result中去,同时把count清零,最后count加1。
public class Solution {
public String countAndSay(int n) {
if(n == 1) {
return 1+"";
}
String result = "1";
for(int i=2;i<=n;i++) {
result = say(result);
}
return result;
}
public String say(String s) {
String result = "";
int count = 0;
char c = '0';
for(int i=0;i<s.length();i++) {
c = s.charAt(i);
if(count !=0 && s.charAt(i-1) != c) {
result = result + count + s.charAt(i-1);
count = 0;
}
count++;
}
result = result + count + c;
return result;
}
}
65. Valid Number
Validate if a given string is numeric.
Some examples:
“0” => true
” 0.1 ” => true
“abc” => false
“1 a” => false
“2e10” => true
Note: It is intended for the problem statement to be ambiguous. You should gather all requirements up front before implementing one.
思路:We start with trimming.
If we see [0-9] we reset the number flags.
We can only see . if we didn’t see e or ..
We can only see e if we didn’t see e but we did see a number. We reset numberAfterE flag.
We can only see + and - in the beginning and after an e
any other character break the validation.
At the and it is only valid if there was at least 1 number and if we did see an e then a number after it as well.
So basically the number should match this regular expression:
[-+]?( ([0-9]+(.[0-9]*)?) | .[0-9]+ ) (e[-+]?[0-9]+)?
public class Solution {
public boolean isNumber(String s) {
s = s.trim();
boolean pointSeen = false;
boolean eSeen = false;
boolean numberSeen = false;
boolean numberAfterE = true;
for(int i=0; i<s.length(); i++) {
if(s.charAt(i) == '-' || s.charAt(i) == '+') {
if(i != 0 && s.charAt(i-1) != 'e') {
return false;
}
}
else if('0' <= s.charAt(i) && s.charAt(i) <= '9') {
numberSeen = true;
numberAfterE = true;
}
else if(s.charAt(i) == '.') {
if(eSeen || pointSeen) {
return false;
}
pointSeen = true;
}
else if(s.charAt(i) == 'e') {
if(eSeen || !numberSeen) {
return false;
}
numberAfterE = false;
eSeen = true;
}
else {
return false;
}
}
return numberSeen && numberAfterE;
}
}
139. Word Break
Given a non-empty string s and a dictionary wordDict containing a list of non-empty words, determine if s can be segmented into a space-separated sequence of one or more dictionary words. You may assume the dictionary does not contain duplicate words.
For example, given
s = “leetcode”,
dict = [“leet”, “code”].
Return true because “leetcode” can be segmented as “leet code”.
思路1:动态规划。把词典放在hashset中,用dp[i]表示前i个字符是否可以切分成单词。dp[i] = dp[j] && dic.contains(s.substring(j+1, i)), 其中0 <= j <= i-1,对于j的循环,找到一个就可以结束。子结构式子里的下标均包含。10ms,击败87.18%。
public class Solution {
public boolean wordBreak(String s, List<String> wordDict) {
if(s == null || wordDict == null) return false;
Set<String> dic = new HashSet<>(wordDict);
boolean[] dp = new boolean[s.length() + 1];
dp[0] = true;
for(int i=1; i<=s.length(); i++) {
for(int j=i-1; j>=0; j--) {
if(dp[j] && dic.contains(s.substring(j, i))) {
dp[i] = true;
break;
}
}
}
return dp[s.length()];
}
}
改进:基于词典中单词长度有限的事实,我们对j的遍历不需要遍历到0。7ms,击败96.44%。
public class Solution {
public boolean wordBreak(String s, List<String> wordDict) {
if(s == null || wordDict == null) return false;
Set<String> dic = new HashSet<>(wordDict);
int maxLen = Integer.MIN_VALUE;
for(String w : dic) {
if(w.length() > maxLen)
maxLen = w.length();
}
boolean[] dp = new boolean[s.length() + 1];
dp[0] = true;
for(int i=1; i<=s.length(); i++) {
for(int j=i-1; j>=0 && i-j <= maxLen; j--) {
if(dp[j] && dic.contains(s.substring(j, i))) {
dp[i] = true;
break;
}
}
}
return dp[s.length()];
}
}
思路2:也是动态规划但是用trie数来存储字典。这里有个技巧是从后往前遍历字符串,这样可以更好地利用trie的结构。8ms,击败93.91%。
public class Solution {
public class TrieNode {
boolean isWord;
TrieNode[] c;
public TrieNode(){
isWord = false;
c = new TrieNode[128];
}
}
public void addWord(TrieNode t, String w) {
for (int i = 0; i < w.length(); i++) {
int j = (int)w.charAt(i);
if (t.c[j] == null) t.c[j] = new TrieNode();
t = t.c[j];
}
t.isWord = true;
}
public boolean wordBreak(String s, Set<String> wordDict) {
TrieNode t = new TrieNode(), cur;
for (String i : wordDict) addWord(t, i);
char[] str = s.toCharArray();
int len = str.length;
boolean[] f = new boolean[len + 1];
f[len] = true;
for (int i = len - 1; i >= 0; i--) {
cur = t;
for (int j = i; cur != null && j < len ; j++) {
cur = cur.c[(int)str[j]];
if (cur != null && cur.isWord && f[j + 1]) {
f[i] = true;
break;
}
}
}
return f[0];
}
}
151. Reverse Words in a String
Given an input string, reverse the string word by word.
For example,
Given s = “the sky is blue”,
return “blue is sky the”.
Update (2015-02-12):
For C programmers: Try to solve it in-place in O(1) space.
click to show clarification.
Clarification:
What constitutes a word?
A sequence of non-space characters constitutes a word.
Could the input string contain leading or trailing spaces?
Yes. However, your reversed string should not contain leading or trailing spaces.
How about multiple spaces between two words?
Reduce them to a single space in the reversed string.
思路:i遍历字数组,j指向字符数组有效部分待填充的位置。设置一个wordCount标志来表明是否是开头,开头的处理情况和非开头不一样,因为非开头的单词的前一个符号是空格。在遍历过程中,记录单词的首尾,反转。最后有效部分整体反转就是所要结果。
public class Solution {
public String reverseWords(String s) {
char[] chars = s.toCharArray();
int n = chars.length;
int i = 0, j = 0, wordCount = 0, wStart = 0;
while(i < n) {
while(i < n && chars[i] == ' ') i++;
if(i == n) break;
if(wordCount != 0) chars[j++] = ' ';
wStart = j;
while(i < n && chars[i] != ' ') chars[j++] = chars[i++];
reverse(chars, wStart, j-1);
wordCount++;
}
reverse(chars, 0, j-1);
return new String(chars).substring(0, j);
}
private void reverse(char[] chars, int start, int end) {
while(start < end) {
char tmp = chars[start];
chars[start++] = chars[end];
chars[end--] = tmp;
}
}
}