首先我们来看一些String的特性。
String对象不可变!
在函数的参数传递中,String传进去时是一种拷贝
String之间用+号非常不好!(+号内部用的是StringBuilder)
StringBuilder
如果想实现将String[] words连接成一个String,那最土的方法就是:
1 2 3 4 String result = "" ; for ( int i = 0 ; i < words.length ; ++i ){ result += words[i]; }
但由于每个+都调用了一次StringBuilder,这无疑增加了很多负担。较好的方式是:
1 2 3 4 5 StringBuilder stringBuilder = new StringBuilder(); for ( int i = 0 ; i < words.length ; ++i ){ stringBuilder.append(words[i]); } String result = stringBuilder.toString();
遍历String的元素
1 2 3 4 String a = "abc" ; for (char c : a.toCharArray()){ }
一些操作
B.indexOf(...)
B.indexOf(A)
: 从B的头看是查找是否存在A
B.indexOf(A, idx)
: 从B的第idx个字符开始查找是否存在A
B.startWith(A)
判断B是否以A开头
B.contains(A)
A是否是B的子串
B.trim()
trim()方法返回调用字符串对象的一个副本,但是所有起始和结尾的空格都被删除了
相关题
Valid Palindrome
判断给定字符串(字符或数字,字母不分大小写)是否是回文(字符串中包含的其它字符可以跳过)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 private boolean ifAlpha (char c) { return !((c >= 'a' && c <= 'z' ) || (c - '0' >= 0 && c - '0' < 10 )); } private boolean helper (String s, int low, int high, boolean deleted) { while (low < high){ if (ifAlpha(s.charAt(low))){ ++low; continue ; } if (ifAlpha(s.charAt(high))){ --high; continue ; } if (s.charAt(low) == s.charAt(high)){ ++low; --high; } else return false ; } return true ; } public boolean isPalindrome (String s) { if (s.length() <= 1 )return true ; return helper(s.toLowerCase(), 0 , s.length() - 1 ,false ); }
Valid Palindrome II
判断给定字符串(只有字母)至多去掉一个后,是否是回文
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 private boolean helper (String s, int low, int high, boolean deleted) { if (high >= s.length()) return false ; while (low < high){ if (s.charAt(low) == s.charAt(high)){ ++low; --high; }else if (!deleted){ return helper(s, low + 1 , high, true )|| helper(s, low, high - 1 , true ); }else return false ; } return true ; } public boolean validPalindrome (String s) { if (s.length() <= 2 )return true ; return helper(s, 0 , s.length() - 1 ,false ); }
Shortest Palindrome
给一个字符串,如果在前面加一些字符,使它变成回文串。求最短的回文串
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 暴力 private boolean helper (String left, String right) { int l = left.length() - 1 ; int r = 0 ; while (l >= 0 && left.charAt(l) == right.charAt(r)){ l--; r++; } return l == -1 ; } public String shortestPalindrome (String s) { if (s.length() <= 1 ) return s; for (int i = s.length() / 2 ; i >= 0 ; --i){ if (2 *i + 2 <= s.length() && s.charAt(i) == s.charAt( i + 1 )){ if (helper(s.substring(0 , i + 1 ), s.substring(i + 1 , 2 *i + 2 ))){ return new StringBuilder(s.substring(2 *i + 2 , s.length())).reverse().toString() + s; } } if (i > 0 && 2 *i + 1 <= s.length() && s.charAt(i-1 ) == s.charAt(i+1 )){ if (helper(s.substring(0 ,i), s.substring(i + 1 ,2 *i + 1 ))){ return new StringBuilder(s.substring(2 *i + 1 , s.length())).reverse().toString() + s; } } } return new StringBuilder(s.substring(1 , s.length())).reverse().toString() + s; }
Largest Palindrome Product
给一个n,判断两个n位数的乘积最大的回文数,输出回文数mod 1337。
对我来说这并不一道easy的题。我自己写了程序,但不知道为什么不对!还超时!气炸了!
先贴上我的错误的 代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 private boolean ifPalindrome (String s, int low, int high) { if (low >= high) return true ; return s.charAt(low) == s.charAt(high) && ifPalindrome(s, low + 1 , high - 1 ); } public int largestPalindrome (int n) { int max = (int )Math.pow(10 ,n) - 1 ; int min = (int )Math.pow(10 , n - 1 ); int n1 = max,n2 = max; while (n1 >= min){ n2 = n1; while (n2 >= min){ BigInteger bigInteger = BigInteger.valueOf(n1).multiply(BigInteger.valueOf(n2)); if (ifPalindrome(bigInteger.toString(),0 , bigInteger.toString().length() - 1 )) return bigInteger.mod(BigInteger.valueOf(1337 )).intValue(); --n2; } --n1; } return 0 ; }
看一下
正确的思路 :
定理:输入范围n∈[1, 8]
,除n = 1
以外,其余n值最大回文数palindrome的位数均为偶数个,可以拆分为half
与reversed(half)
左右两半
思路:从上界high = pow(10, n) - 1
向下界low = pow(10, n - 1)
枚举half,构造回文,检查是否存在两个n位数的除数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public int largestPalindrome (int n) { if (n == 1 ) return 9 ; int high = (int ) Math.pow(10 , n) - 1 , low = high / 10 ; for (int i = high; i > low; i--) { long palindrome = createPalindrome(i); for (long j = high; j > low; j--) { if (palindrome / j > high) break ; if (palindrome % j == 0 ) return (int ) (palindrome % 1337 ); } } return -1 ; } private long createPalindrome (long num) { String str = num + new StringBuilder(Long.toString(num)).reverse().toString(); return Long.parseLong(str); }
优化
当然了,以上操作还可以优化!
就是在探索时,只需要探索一半啊我曹!
1 2 3 4 5 6 for (long j = high; j > low; j--) 这里!改成! for (long j = high; j*j > palindrome; j--)
速度提升了二倍啊!
骚操作
当然了,这些都没啥。下面这个骚操作,能把人气死。呵呵哒
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public int largestPalindrome (int n) { switch (n){ case 1 : return 9 ; case 2 : return 987 ; case 3 : return 123 ; case 4 : return 597 ; case 5 : return 677 ; case 6 : return 1218 ; case 7 : return 877 ; case 8 : return 475 ; } return 0 ; }
String to Integer (atoi)
这道题题意实在是太不明朗了!
大原则:将str转化为int
小原则:
忽略开始的空格
可能有正负号
如果出现了一个异常字符,就丢弃这个异常字符及其之后的东西
如果没有解,就返回0
如果正数溢出,就返回正整数INT_MAX (2147483647)
;如果负数溢出,就返回负整数INT_MIN (-2147483648)
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 public int myAtoi (String str) { int start = 0 ; while (start < str.length() && str.charAt(start) == ' ' ) ++start; if (start >= str.length()) return 0 ; int sig = 1 ; if (str.charAt(start) == '+' ){++start;sig = 1 ;} else if (str.charAt(start) == '-' ){++start; sig = -1 ;} StringBuilder sb = new StringBuilder(); while (start < str.length()){ int num = str.charAt(start) - '0' ; if (num >= 0 && num < 10 ){ sb.append(str.charAt(start)); }else { break ; } ++start; } if (sb.length() == 0 ) return 0 ; try { return sig * Integer.valueOf(sb.toString()); }catch (Exception e){ if (sig > 0 ) return 2147483647 ; else return -2147483648 ; } }
Roman to Integer
将罗马数字转化为整数
罗马数字的规则:
罗马数字共有7个,即Ⅰ(1)、Ⅴ(5)、Ⅹ(10)、Ⅼ(50)、Ⅽ(100)、Ⅾ(500)和Ⅿ(1000)
重复数次:一个罗马数字重复几次,就表示这个数的几倍
右加左减:
在较大的罗马数字的右边记上较小的罗马数字,表示大数字加小数字。
在较大的罗马数字的左边(只能有一位)记上较小的罗马数字,表示大数字减小数字。
思路:输入一个数后,我们可以来遍历这个数,用sum来总计和,比较i和i+1,如果,i+1比i小的话,直接相加,如果i大于i+1的话,则将总和sum减去i这个地方数的两倍,同时加上i+1 就相当于后边的数比左边的数大,则用右边的数减左边的数。但因为之前已经加过一次了,所以减两次
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 int [] nums = {1000 ,500 ,100 ,50 ,10 ,5 ,1 }; char [] chars = {'M' ,'D' ,'C' ,'L' ,'X' ,'V' ,'I' }; HashMap<Character, Integer> roman = new HashMap<>(); public int romanToInt (String s) { for (int i = 0 ; i < chars.length; ++i){ roman.put(chars[i], nums[i]); } int sum = roman.get(s.charAt(0 )); for (int i = 1 ; i < s.length(); ++i){ sum += roman.get(s.charAt(i)); if (roman.get(s.charAt(i)) > roman.get(s.charAt(i-1 ))){ sum -= 2 *roman.get(s.charAt(i-1 )); } } return sum; }
Integer to Roman
将整数转化为罗马数字
罗马数字规律:
大部分时候用字符相叠加来表示数字。I是1, II是2, III是3。VI是6(挨个看来,是“5 和 1”的组合),VII是7,VIII是8。
含有10的字符(I,X,C和M)最多可以重复出现三个。为了表示4,必须用同一位数的下一个更大的数字5来减去一。不能用IIII来表示4,而应该是IV(意思是比5小1)。40写做XL(比50小10),41写做XLI,42写做XLII,43写做XLIII,44写做XLIV(比50小10并且比5小1)。
有些时候表示方法恰恰相反。为了表示一个中间的数字,需要从一个最终的值来减。比如:9需要从10来减:8是VIII,但9确是IX(比10小1),并不是VIII(I字符不能重复4次)。90是XC,900是CM。
表示5的字符不能在一个数字中重复出现。10只能用X表示,不能用VV表示。100只能用C表示,而不是LL。
罗马数字是从左到右来计算,因此字符的顺序非常重要。DC表示600,而CD完全是另一个数字400(比500小100)。CI是101,IC不是一个罗马数字(因为你不能从100减1,你只能写成XCIX,表示比100小10,且比10小1)。
有了以上规则,按照规则挨个位置计算就好啦
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 public String intToRoman (int num) { char [][] roman = { {'M' ,'D' ,'C' }, {'C' ,'L' ,'X' }, {'X' ,'V' ,'I' } }; StringBuilder sb = new StringBuilder(); int digit = num/1000 ; num -= digit*1000 ; while (digit > 0 ){ sb.append('M' ); --digit; } int [] divisor = {100 ,10 ,1 }; for (int i = 0 ; i < 3 ; ++i) { digit = num / divisor[i]; num -= digit * divisor[i]; if (digit == 9 ) { sb.append(roman[i][2 ]); sb.append(roman[i][0 ]); } else if (digit >= 5 ) { sb.append(roman[i][1 ]); digit--; while (digit >= 5 ) { sb.append(roman[i][2 ]); digit--; } } else if (digit == 4 ) { sb.append(roman[i][2 ]); sb.append(roman[i][1 ]); } else { while (digit > 0 ) { sb.append(roman[i][2 ]); digit--; } } } return sb.toString(); }
Integer to English Words
将数字转化为英文读音
读音规则:
三位一体,每三位都是同一个读法,只是在后面加thunsand/Million/Billion即可。例如123,456,789可以分解为:123 million, 456 thunsand, 789
三位数字读法:百位直接就是数字 + thunsand; 十位和个位比较麻烦。20以下的数字直接读,20以上的数字用 几十 + 几的方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 if (num == 0 ) return "Zero" ; String[] splitWords = {"" ,"Thousand" ,"Million" ,"Billion" }; String[] key1 = {"" ,"One" ,"Two" ,"Three" ,"Four" ,"Five" ,"Six" ,"Seven" ,"Eight" ,"Nine" ,"Ten" , "Eleven" ,"Twelve" ,"Thirteen" ,"Fourteen" ,"Fifteen" ,"Sixteen" ,"Seventeen" ,"Eighteen" ,"Nineteen" ,"Twenty" }; String[] key2 = {"" ,"" ,"Twenty" ,"Thirty" ,"Forty" ,"Fifty" ,"Sixty" ,"Seventy" ,"Eighty" ,"Ninety" }; int counter = 0 ; StringBuilder sb_global = new StringBuilder(); while (num > 0 ){ StringBuilder sb = new StringBuilder(); int last = num - 1000 *(num/1000 ); if (last == 0 ){ counter += 1 ; num = num/1000 ; continue ; } int hundred_digit = last/100 ; if (hundred_digit > 0 ){ sb.append(" " ); sb.append(key1[hundred_digit]); sb.append(" Hundred" ); } int low_two_digit = last - hundred_digit*100 ; if (low_two_digit > 0 ) { if (low_two_digit <= 20 ) { sb.append(" " ); sb.append(key1[low_two_digit]); } else { int tens = low_two_digit / 10 ; int units = low_two_digit - tens * 10 ; if (tens > 0 ) { sb.append(" " ); sb.append(key2[tens]); } if (units > 0 ) { sb.append(" " ); sb.append(key1[units]); } } } if (counter > 0 ) { sb.append(" " ); sb.append(splitWords[counter]); } sb_global.insert(0 , sb.toString()); counter += 1 ; num = num/1000 ; } return sb_global.subSequence(1 , sb_global.length()).toString(); }
代码简化:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 private final String[] LESS_THAN_20 = {"" , "One" , "Two" , "Three" , "Four" , "Five" , "Six" , "Seven" , "Eight" , "Nine" , "Ten" , "Eleven" , "Twelve" , "Thirteen" , "Fourteen" , "Fifteen" , "Sixteen" , "Seventeen" , "Eighteen" , "Nineteen" };private final String[] TENS = {"" , "Ten" , "Twenty" , "Thirty" , "Forty" , "Fifty" , "Sixty" , "Seventy" , "Eighty" , "Ninety" };private final String[] THOUSANDS = {"" , "Thousand" , "Million" , "Billion" };public String numberToWords (int num) { if (num == 0 ) return "Zero" ; int i = 0 ; String words = "" ; while (num > 0 ) { if (num % 1000 != 0 ) words = helper(num % 1000 ) +THOUSANDS[i] + " " + words; num /= 1000 ; i++; } return words.trim(); } private String helper (int num) { if (num == 0 ) return "" ; else if (num < 20 ) return LESS_THAN_20[num] + " " ; else if (num < 100 ) return TENS[num / 10 ] + " " + helper(num % 10 ); else return LESS_THAN_20[num / 100 ] + " Hundred " + helper(num % 100 ); }
Group Anagrams
给str[]
,将同一排列组合的str分为一组。
一开始想多了,其实挺简单的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 private String toHash (String s) { int [] hash = new int [26 ]; for (int i = 0 ; i < s.length(); ++i){ ++hash[s.charAt(i) - 'a' ]; } StringBuilder sb = new StringBuilder(); for (int i : hash)sb.append(i); return sb.toString(); } public List<List<String>> groupAnagrams(String[] strs) { if (strs.length == 0 ) return new LinkedList<List<String>>(); HashMap<String, List<String>> set = new HashMap<>(); for (String s:strs){ String hash = toHash(s); if (set.containsKey(hash)){ set.get(hash).add(s); }else { List<String> list = new LinkedList<>(); list.add(s); set.put(hash,list); } } return new LinkedList<List<String>>(set.values()); }
Mini Parser
好复杂的一题。将括号的lists反序列化。很复杂!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 class Node { NestedInteger nestedInteger; int end; Node(NestedInteger nestedInteger, int end){ this .nestedInteger = nestedInteger; this .end = end; } } private Node helper (String s, int idx) { if (idx == s.length()) return null ; NestedInteger obj = new NestedInteger(); int start = idx; int addCounter = 0 ; int i = idx; for (i = idx; i < s.length(); ++i){ if (s.charAt(i) == ',' ){ addCounter+= 1 ; if (start < i)obj.add(new NestedInteger(Integer.valueOf(s.substring(start, i)))); start = i + 1 ; }else if (s.charAt(i) == '[' ){ Node next = helper(s, i + 1 ); if (next != null ) { obj.add(next.nestedInteger); addCounter+= 1 ; i = next.end; if (i < s.length())--i; start = next.end + 1 ; } }else if (s.charAt(i)== ']' ){ addCounter+= 1 ; if (start < i)obj.add(new NestedInteger(Integer.valueOf(s.substring(start, i)))); ++i; break ; } } return new Node(obj, i); } public NestedInteger deserialize (String s) { if (s.length() == 0 ) return new NestedInteger(); if (s.length() == 1 || s.charAt(0 ) != '[' ) return new NestedInteger(Integer.valueOf(s)); return helper(s,1 ).nestedInteger; }
又用栈写了一遍:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 public int getNumEnd (String s, int idx) { while (idx < s.length() && (s.charAt(idx) == '+' || s.charAt(idx) == '-' || Character.isDigit(s.charAt(idx)))){ ++idx; } return idx; } public NestedInteger deserialize (String s) { if (s.length() == 0 || s.charAt(0 ) != '[' ) return new NestedInteger(Integer.valueOf(s)); Stack<NestedInteger> stack = new Stack<>(); NestedInteger result = null ; for (int i = 0 ; i < s.length();){ if (s.charAt(i) == ',' ){ ++i; }else if (s.charAt(i) == '[' ){ NestedInteger curr = new NestedInteger(); if (!stack.empty()) stack.peek().getList().add(curr); stack.push(curr); ++i; }else if (s.charAt(i) == ']' ){ result = stack.pop(); ++i; }else { int numEnd = getNumEnd(s, i); int sig = 1 ; if (s.charAt(i) == '-' ){ sig = -1 ; ++i; }else if (s.charAt(i) == '+' ){ ++i; } int num = sig * Integer.valueOf(s.substring(i, numEnd)); NestedInteger curr = new NestedInteger(num); stack.peek().getList().add(curr); i = numEnd; } } while (!stack.empty()){ result = stack.pop(); } return result; }
Validate IP Address
验证给定字符串是否是IPV4或IPV6地址
这道题实在是太心酸了。主要有以下几个注意点:
split
操作会忽略最后一个字符为分隔符的情况
IPv4 :
长度必须为4
每个字符不可有有前导0
第一个字符不可以为0
不可以出现其他字符
值不可以大于255
IPv6:
长度要么为8,要么 只存在一个::
。(通过使用双冒号(::)替换一系列零来指定 IPv6 地址。例如,IPv6 地址 ff06:0:0:0:0:0:0:c3 可写作 ff06::c3。一个 IP 地址中只可使用一次双冒号。
可以有前导零
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 private String validIPv4 (String[] ipv4) { try { for (int i = 0 ; i < ipv4.length; ++i){ if (ipv4[i].length() == 0 )return "Neither" ; if (ipv4[i].length() > 1 && ipv4[i].charAt(0 ) == '0' )return "Neither" ; for (char c : ipv4[i].toCharArray()) if (c < '0' || c > '9' ) return "Neither" ; int val = Integer.valueOf(ipv4[i]); if (i == 0 && val == 0 )return "Neither" ; if (val < 0 || val >= 256 ) return "Neither" ; } return "IPv4" ; }catch (NumberFormatException e){ return "Neither" ; } } private String validIPv6 (String[] IPv6) { boolean lenOK = false ; for (int i = 0 ; i < IPv6.length; ++i){ if (IPv6[i].length()==0 ) { if (lenOK) return "Neither" ; lenOK = true ; if (i > 0 ){ if (IPv6[i-1 ].length() == 0 )return "Neither" ; else if (IPv6[i-1 ].length() == 1 ) { if (IPv6[i - 1 ].equals("0" )) return "Neither" ; }else { boolean allZero = true ; for (char c : IPv6[i - 1 ].toCharArray()){ if (c != '0' ){ allZero = false ; break ; } } if (allZero)return "Neither" ; } } } if (IPv6[i].length() > 4 )return "Neither" ; for (char c : IPv6[i].toCharArray()){ if ((c >= 'a' && c <= 'f' )|| (c >='0' && c <= '9' )|| (c >= 'A' && c <= 'F' )){ }else { return "Neither" ; } } } if ((lenOK && IPv6.length < 8 ) || IPv6.length == 8 ) return "IPv6" ; else return "Neither" ; } public String validIPAddress (String IP) { if (IP.length() == 0 ) return "Neither" ; char last = IP.charAt(IP.length() - 1 ); if (!((last >= 'a' && last <= 'f' )|| (last >='0' && last <= '9' )|| (last >= 'A' && last <= 'F' ))){ return "Neither" ; } String[] ipv4 = IP.split("\\." ); if (ipv4.length == 4 ){ return validIPv4(ipv4); } String[] IPv6 = IP.split(":" ); if (IPv6.length > 0 ){ return validIPv6(IPv6); } return "Neither" ; }
自己写的代码好乱,学习一下别人的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 public String validIPAddress (String IP) { if (IP.length() == 0 ) return "Neither" ; int lastChar = IP.charAt(IP.length() - 1 ); if (lastChar == ':' || lastChar == '.' ) return "Neither" ; String[] groups = IP.split("\\." ); if (groups.length == 1 ) { groups = IP.split(":" ); if (validateIPv6(groups)) return "IPv6" ; else return "Neither" ; } else { if (validateIPv4(groups)) return "IPv4" ; else return "Neither" ; } } private boolean validateIPv4Group (String group) { if (group.length() > 3 || group.length() == 0 ) return false ; if (group.length() > 1 && group.charAt(0 ) == '0' ) return false ; int res = 0 ; for (int i = 0 ; i < group.length(); i++) { char c = group.charAt(i); if (c >= '0' && c <= '9' ) res = res * 10 + c - '0' ; else return false ; } return res < 256 ; } private boolean validateIPv4 (String[] groups) { if (groups.length != 4 ) return false ; for (int i = 0 ; i < groups.length; i++) { if (!validateIPv4Group(groups[i])) return false ; } return true ; } private boolean validateIPv6Group (String group) { if (group.length() > 4 || group.length() == 0 ) return false ; for (int i = 0 ; i < group.length(); i++) { char c = Character.toLowerCase(group.charAt(i)); if ((c >= '0' && c <= '9' ) || (c >= 'a' && c <= 'f' )) continue ; else return false ; } return true ; } private boolean validateIPv6 (String[] groups) { if (groups.length != 8 ) return false ; for (int i = 0 ; i < 8 ; i++) { if (!validateIPv6Group(groups[i])) return false ; } return true ; } }
Compare Version Numbers
比较两个str的版本号的大小。
注意:有1.0000.00
与1.0
的这种变态样例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public int compareVersion (String version1, String version2) { String[] v1 = version1.split("\\." ); String[] v2 = version2.split("\\." ); int v1_idx = 0 , v2_idx = 0 ; while (v1_idx < v1.length && v2_idx < v2.length){ if (Integer.valueOf(v1[v1_idx]) == Integer.valueOf(v2[v2_idx])){++v1_idx; ++v2_idx;} else if (Integer.valueOf(v1[v1_idx]) > Integer.valueOf(v2[v2_idx])){return 1 ;} else return -1 ; } if (v1_idx < v1.length){ while (v1_idx < v1.length && Integer.valueOf(v1[v1_idx]) == 0 ){++v1_idx;} if (v1_idx == v1.length) return 0 ; else return 1 ; } if (v2_idx < v2.length){ while (v2_idx < v2.length && Integer.valueOf(v2[v2_idx]) == 0 ){++v2_idx;} if (v2_idx == v2.length) return 0 ; else return -1 ; } return 0 ; }
Reverse Words in a String
将string里的单词顺序搞反。需要注意的是:如果有多个连续空格,则认为是一个空格。结果不可以出现前导空格或者后缀空格。
这道题一开始想多了。其实挺简单的。
1 2 3 4 5 6 7 8 9 10 11 public String reverseWords (String s) { if (s.length() == 0 ) return s; String[] strs = s.split(" +" ); StringBuilder sb = new StringBuilder(); for (int i = strs.length - 1 ; i >= 0 ; --i){ if (strs[i].length() == 0 )continue ; sb.append(' ' ); sb.append(strs[i]); } return sb.length() == 0 ? "" : sb.subSequence(1 , sb.length()).toString(); }
Reverse Words in a String II
将输入的string改成char[]的形式,要求用O(1)空间复杂度将单词顺序搞反。这次没有多个空格,也没有前导和后缀空格。
思路:
先将全部字符串翻转
然后对每个单词进行翻转
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 private void swap (char [] str, int low, int high) { char temp =str[low]; str[low] = str[high]; str[high] = temp; } public void reverseWords (char [] str) { int low = 0 , high = str.length - 1 ; while (low < high){ swap(str, low, high); ++low; --high; } int i = 0 ; low = i;high = i; char space = ' ' ; while (i < str.length){ if (str[i] == space){++i;} else { low = i; ++i; while (i < str.length && str[i] != space){ ++i; } high = i - 1 ; while (low < high){ swap(str, low, high); ++low; --high; } } } }
其实以上代码可以简化:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public void reverseWords (char [] str) { reverse(str, 0 , str.length-1 ); int r = 0 ; while (r < str.length){ int l = r; while (r < str.length && str[r] != ' ' ) r++; reverse(str, l, r-1 ); r++; } } public void reverse (char [] s, int l, int r) { while (l < r){ char tmp = s[l]; s[l++] = s[r]; s[r--] = tmp; } }
Reverse Words in a String III
这道题其实是上一题的easy版。要将字符串内的每个单词的单词内部顺序搞反。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 private void reverse (char [] str, int low, int high) { while (low < high) { char temp = str[low]; str[low] = str[high]; str[high] = temp; ++low; --high; } } public String reverseWords (String s) { char [] str = s.toCharArray(); int i = 0 ; int low = i; int high = i; char space = ' ' ; while (i < str.length){ if (str[i] == space){++i;} else { low = i; ++i; while (i < str.length && str[i] != space){ ++i; } high = i - 1 ; reverse(str, low, high); } } return String.valueOf(str); }
Excel Sheet Column Title
将数字表示成Excel中的字母:
1 2 3 4 5 6 7 1 -> A 2 -> B 3 -> C ... 26 -> Z 27 -> AA 28 -> AB
这道题的棘手之处在于没有0元素
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 private String helper (int n) { if (n > 26 ){ if (n%26 == 0 ){ return helper(n/26 - 1 ) + helper((n - 26 *(n/26 ) == 0 ? 25 :n - 26 *(n/26 ) ) + 1 ); }else { return helper(n/26 ) + helper(n - 26 *(n/26 )); } }else { return String.valueOf((char )(n+64 )); } } public String convertToTitle (int n) { return helper(n); }
代码写的太绕了,看看别人的:
1 2 3 4 5 6 7 8 9 10 public String convertToTitle (int n) { StringBuilder sb = new StringBuilder(); while (n>0 ){ n--; sb.insert(0 ,(char )('A' +n%26 )); n=n/26 ; } return sb.toString(); }
Excel Sheet Column Number
将Excel中的字母表示成数字。
做完上一题,这一题就简单很多。
1 2 3 4 5 6 7 8 public int titleToNumber (String s) { int num = 0 ; for (char c : s.toCharArray()){ num *= 26 ; num += (c - 'A' + 1 ); } return num; }
Bulls and Cows
甲乙二人玩猜数字游戏。甲心里默认一个数字,乙去猜。令A = 乙完全猜中(位置和数字都猜中)的个数。B = 乙猜中数字但没猜对位置的个数。统计A和B。
1 2 3 4 Secret number: "1123" Friend's guess: "0111" 输出 "1A1B"
思路很简单,就是hash。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public String getHint (String secret, String guess) { int [] secret_hash = new int [10 ]; int [] guess_hash = new int [10 ]; int A = 0 ; for (int i = 0 ; i < secret.length(); ++i){ if (secret.charAt(i) == guess.charAt(i)){ ++A; }else { ++secret_hash[secret.charAt(i) - '0' ]; ++guess_hash[guess.charAt(i) - '0' ]; } } int B = 0 ; for (int i = 0 ; i < 10 ; ++i){ if (secret_hash[i] > 0 && guess_hash[i] > 0 ){ B += Math.min(secret_hash[i], guess_hash[i]); } } return String.valueOf(A)+"A" + String.valueOf(B) + "B" ; }
Is Subsequence
给两个str : s 和t 。判断s是否是t的子序列
思路:如果s中的每个元素在t中的idx可以构成一个升序的话,那么s就是t的子序列
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 private int [][] toHash(String s){ int [][] hash = new int [26 ][s.length() + 1 ]; for (int i = 0 ; i < s.length(); ++i){ ++hash[s.charAt(i) - 'a' ][0 ]; hash[s.charAt(i) - 'a' ][hash[s.charAt(i) - 'a' ][0 ]] = i; } return hash; } public boolean isSubsequence (String s, String t) { if (s.length() > t.length()) return false ; int [][] t_hash = toHash(t); int last = -1 ; for (char c : s.toCharArray()){ if (t_hash[c-'a' ][0 ] == 0 ) return false ; int curr = findLarger(t_hash[c-'a' ], last); if (curr > last){ last = curr; } else return false ; } return true ; } private int findLarger (int [] tIdxs, int last) { if (tIdxs[tIdxs[0 ]] < last) return -1 ; return bitSeach(tIdxs, 1 , tIdxs[0 ], last); } private int bitSeach (int [] nums, int low, int high, int target) { int mid; while (low + 1 < high){ mid = low + (high - low) / 2 ; if (nums[mid] > target){ high = mid; }else { low = mid; } } if (nums[low] > target) return nums[low]; if (nums[high] > target) return nums[high]; return -1 ; }
优化
在集合里寻找较大的值,可以用treeset
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 public boolean isSubsequence1 (String s, String t) { if (s == null || t == null ) return false ; if (s.length() > t.length()) return false ; if (s.equals("" )) return true ; HashMap<Character, TreeSet<Integer>> map = new HashMap<>(); for (int i = 0 ; i < t.length(); i++) { char ch = t.charAt(i); TreeSet<Integer> list = map.getOrDefault(ch, new TreeSet<Integer>()); list.add(i); map.put(ch, list); } Integer pre = -1 ; for (int i = 0 ; i < s.length(); i++) { char ch = s.charAt(i); if (!map.containsKey(ch)) return false ; Integer temp = pre; pre = map.get(ch).ceiling(pre); if (pre != null && pre.equals(temp)) { pre = map.get(ch).ceiling(pre + 1 ); } if (pre == null || pre.intValue() <= temp.intValue()) { return false ; } } return pre != -1 ; }
Longest Substring with At Least K Repeating Characters
给一个字符串和一个k。求子串的最大长度:子串的每个字符的出现次数都大于等于k。
思路:分治
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 private int helper (int low, int high) { if (high - low + 1 < k) return 0 ; for (int i = low; i <= high; ++i){ int charNum = hash[high][s_chars[i] - 'a' ] - (low > 0 ? hash[low - 1 ][s_chars[i] - 'a' ] : 0 ); if (charNum > 0 && charNum < k){ int left = helper(low, i-1 ); int right = helper(i + 1 , high); return Math.max(left, right); } } return high - low + 1 ; } int [][] hash; char [] s_chars; int k; public int longestSubstring (String s, int k) { if (s.length() == 0 || k == 0 ) return 0 ; if (s.length() == 1 ){ if (k == 1 ) return 1 ; else return 0 ; } s_chars = s.toCharArray(); this .k = k; hash = new int [s.length()][26 ]; ++hash[0 ][s.charAt(0 ) - 'a' ]; for (int i = 1 ; i < s.length(); ++i){ for (int j = 0 ; j < 26 ; ++j){ hash[i][j] = hash[i-1 ][j]; } ++hash[i][s.charAt(i) - 'a' ]; } return helper(0 , s.length() - 1 ); }
骚操作:双指针
是真的没看懂。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 public int longestSubstring (String s, int k) { char [] str = s.toCharArray(); int [] counts = new int [26 ]; int h, i, j, idx, max = 0 , unique, noLessThanK; for (h = 1 ; h <= 26 ; h++) { Arrays.fill(counts, 0 ); i = 0 ; j = 0 ; unique = 0 ; noLessThanK = 0 ; while (j < str.length) { if (unique <= h) { idx = str[j] - 'a' ; if (counts[idx] == 0 ) unique++; counts[idx]++; if (counts[idx] == k) noLessThanK++; j++; } else { idx = str[i] - 'a' ; if (counts[idx] == k) noLessThanK--; counts[idx]--; if (counts[idx] == 0 ) unique--; i++; } if (unique == h && unique == noLessThanK) max = Math.max(j - i, max); } } return max; }
巧用unicode编码做Hash
小写字符只有26个。因此可以申请长度为26的int[]做hash。
如果不一定大小写,可以用256大小的数组做hash。因为最大的Z的unicode不超过256.
Isomorphic Strings
判断两个字符串是否是同构的。
思路:如果是同构的,那么如果第i位置的字符曾经出现过,那么两个字符的曾经出现过的位置一定是一样的。
1 2 3 4 5 6 7 8 9 10 11 12 13 public boolean isIsomorphic (String s, String t) { int [] s_hash = new int [256 ]; int [] t_hash = new int [256 ]; Arrays.fill(s_hash, -1 ); Arrays.fill(t_hash, -1 ); for (int i = 0 ; i < s.length(); ++i){ if (s_hash[s.charAt(i)] != t_hash[t.charAt(i)]) return false ; s_hash[s.charAt(i)] = i; t_hash[t.charAt(i)] = i; } return true ; }
加减乘除问题
Additive Number
给一个字符串,判断它是否是additive序列。其中additive序列的含义是前两个元素的和等于本元素。
要点:一个元素可能由多位的char组成。因此比较麻烦。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 private String add (String a, String b) { int a_idx = a.length() - 1 , b_idx = b.length() - 1 ; StringBuilder sb = new StringBuilder(); int lastMod = 0 ; while (a_idx >= 0 || b_idx >= 0 ){ int sum = lastMod; if (a_idx >= 0 ){ sum += Integer.valueOf(a.charAt(a_idx) - '0' ); --a_idx; } if (b_idx >= 0 ){ sum += Integer.valueOf(b.charAt(b_idx) - '0' ); --b_idx; } lastMod = sum/10 ; sum %= 10 ; sb.append(sum); } if (lastMod > 0 ) sb.append(lastMod); return sb.reverse().toString(); } private boolean dfs (String num, String lastNum, String currNum, int end) { if (end == num.length()) return true ; if ((lastNum.length() > 1 && lastNum.charAt(0 ) == '0' ) || (currNum.length() > 1 && currNum.charAt(0 ) == '0' )) return false ; String sum = add(currNum, lastNum); if (end + sum.length() <= num.length() && sum.equals(num.substring(end, end + sum.length()))){ if (dfs(num, currNum, sum , end + sum.length())) return true ; } return false ; } public boolean isAdditiveNumber (String num) { if (num.length() < 3 ) return false ; for (int i = 1 ; i < num.length(); ++i){ for (int j = i + 1 ; j < num.length(); ++j){ if (dfs(num, num.substring(0 ,i), num.substring(i,j),j)){ return true ; }; } } return false ; }
Different Ways to Add Parentheses
给一个算式 "2-1-1"
. 这个算式可能有两种解法:
1 2 ((2-1)-1) = 0 (2-(1-1)) = 2
那么就输出[0,2]。实现这个功能
思路:分治。一个符号将两边的数字分成了两部分!这道题还是做了比较久的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 [ left ] + [ right ] 上面所示的加号就把运算分成了两部分,一个是left,一个是right 而left和right的内部也可以这么分 private int getNumEndIdx (String s, int idx) { while (idx < s.length() && Character.isDigit(s.charAt(idx))){ ++idx; } return idx; } private int operation (int a, int b, char operator) { switch (operator){ case '+' :{return a+b;} case '-' :{return a-b;} case '*' :{return a*b;} } return 0 ; } char [] operators_arr; int [] nums_arr; private void split (String s) { List<Character> operators = new ArrayList<>(); operators.add('+' ); List<Integer> nums = new ArrayList<>(); int i = 0 ; int numEnd; while (i < s.length()){ if (!Character.isDigit(s.charAt(i))){ operators.add(s.charAt(i)); ++i; }else { numEnd = getNumEndIdx(s, i); nums.add(Integer.valueOf(s.substring(i, numEnd))); i = numEnd; } } operators_arr = new char [operators.size()]; nums_arr = new int [nums.size()]; for (i = 0 ; i < operators_arr.length; ++i){ operators_arr[i] = operators.get(i); nums_arr[i] = nums.get(i); } } List<Integer>[][][] memo; private List<Integer> divide (int start, int end, int operatorIdx) { if (memo[start][end][operatorIdx] != null ) return memo[start][end][operatorIdx]; List<Integer> all = new LinkedList<>(); if (start == end){ all.add(nums_arr[start]); memo[start][end][operatorIdx] = all; return all; } if (start == end - 1 ){ all.add(operation(nums_arr[start], nums_arr[end], operators_arr[end])); memo[start][end][operatorIdx] = all; return all; } List<Integer> left = new LinkedList<>(); for (int leftOperatorIdx = start + 1 ; leftOperatorIdx < operatorIdx; ++leftOperatorIdx){ left.addAll(divide(start, operatorIdx - 1 ,leftOperatorIdx)); } if (left.size() == 0 ){ left.add(nums_arr[start]); } List<Integer> right = new LinkedList<>(); for (int rightOperatorIdx = operatorIdx + 1 ; rightOperatorIdx <= end; ++rightOperatorIdx){ right.addAll(divide(operatorIdx, end, rightOperatorIdx)); } if (right.size() == 0 && operatorIdx == end){ right.add(nums_arr[end]); } for (int leftElement : left){ for (int rightElement : right){ all.add(operation(leftElement, rightElement, operators_arr[operatorIdx])); } } memo[start][end][operatorIdx] = all; return all; } public List<Integer> diffWaysToCompute (String input) { List<Integer> result = new LinkedList<>(); if (input.length() == 0 ) return result; split(input); memo = new List[operators_arr.length][operators_arr.length][operators_arr.length]; for (int operatorIdx = 1 ; operatorIdx < operators_arr.length; ++operatorIdx){ result.addAll( divide(0 , operators_arr.length - 1 , operatorIdx)); } if (result.size() == 0 ){ result.add(Integer.valueOf(input)); } return result; }
我的代码好复杂。。。因为我总觉得字符串截取的复杂度很高,因此将字符串预处理为数组。
看看别人的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 public List<Integer> diffWaysToCompute (String input) { List<Integer> ret = new LinkedList<Integer>(); for (int i=0 ; i<input.length(); i++) { if (input.charAt(i) == '-' || input.charAt(i) == '*' || input.charAt(i) == '+' ) { String part1 = input.substring(0 , i); String part2 = input.substring(i+1 ); List<Integer> part1Ret = diffWaysToCompute(part1); List<Integer> part2Ret = diffWaysToCompute(part2); for (Integer p1 : part1Ret) { for (Integer p2 : part2Ret) { int c = 0 ; switch (input.charAt(i)) { case '+' : c = p1+p2; break ; case '-' : c = p1-p2; break ; case '*' : c = p1*p2; break ; } ret.add(c); } } } } if (ret.size() == 0 ) { ret.add(Integer.valueOf(input)); } return ret; }
Fraction Addition and Subtraction
计算分数的加减。例如给"-1/2+1/2+1/3"
,要给出结果"1/3"
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 class Solution { public String fractionAddition (String expression) { int firstEnd = getFraction(expression, 1 ); String first = expression.substring(0 , firstEnd); while (firstEnd < expression.length()){ char sig = expression.charAt(firstEnd); int secondEnd = getFraction(expression, firstEnd + 1 ); first = operator(first, expression.substring(firstEnd + 1 , secondEnd), sig); firstEnd = secondEnd; } return first; } char add = '+' ; char sub = '-' ; private String operator (String first, String second, char sig) { int [] firsts = getInt(first); int [] seconds = getInt(second); int [] result = new int [2 ]; result[1 ] = firsts[1 ]*seconds[1 ]; firsts[0 ] = firsts[0 ] * seconds[1 ]; seconds[0 ] = seconds[0 ] * firsts[1 ]; if (sig == add){ result[0 ] = firsts[0 ] + seconds[0 ]; }else { result[0 ] = firsts[0 ] - seconds[0 ]; } if (result[1 ] < 0 ){ result[0 ] = -result[0 ]; result[1 ] = -result[1 ]; } int d = Gcd(Math.abs(result[0 ]), Math.abs(result[1 ])); result[0 ] /= d; result[1 ] /= d; return result[0 ] + "/" + result[1 ]; } private int Gcd (int a, int b) { while (b != 0 ){ int r = b; b = a % b; a = r; } return a; } private int [] getInt(String str){ int [] result = new int [2 ]; String[] strs = str.split("/" ); result[0 ] = Integer.valueOf(strs[0 ]); if (strs.length > 1 )result[1 ] = Integer.valueOf(strs[1 ]); else result[1 ] = 1 ; return result; } private int getFraction (String expression, int idx) { while (idx < expression.length()){ if (expression.charAt(idx) == add || expression.charAt(idx) == sub){ break ; } ++idx; } return idx; } }