说明:算法背景和解法来自https://github.com/ketao1989/The-Art-Of-Programming-By-July.git ,如有版权问题,请留言告知!
最近看博客,发现一些有趣的算法,很久没接触,都不清楚了。这里,把上述的github中一些算法用java实现。
背景 给定一个字符串,要求把字符串前面的若干个字符移动到字符串的尾部,如把字符串“abcddg”前面的2个字符’a’和’b’移动到字符串的尾部,使得原字符串变成字符串“cdcgab”。请写一个函数完成此功能,要求对长度为n的字符串操作的时间复杂度为 O(n),空间复杂度为 O(1)。
Java 代码 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 public class RotateString { public static void rotateString (char [] str,int n,int len) { reverseString(str,0 ,n-1 ); reverseString(str,n,len-1 ); reverseString(str,0 ,len-1 ); } private static void reverseString (char [] str,int from,int to) { while (from < to){ char ch = str[from]; str[from++] = str[to]; str[to--] = ch; } } public static void main (String[] args) { char [] str = {'a' ,'b' ,'c' ,'d' ,'c' ,'g' }; rotateString(str,2 ,6 ); System.out.println(str); } }
背景 给定两个分别由字母组成的字符串A和字符串B,字符串B的长度比字符串A短。请问,如何最快地判断字符串B中所有字母是否都在字符串A里?简单起见,暂不考虑非字符符号。
Java 代码 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 public class StringContain { public static boolean isContainString (String longger,String shortter) throws Exception { long hash = 0L ; for (int i =0 ;i<longger.length();i++){ hash |= (1L << computeLetterBit(longger.charAt(i))); } for (int i =0 ; i<shortter.length();i++){ if ((hash & (1L << computeLetterBit(shortter.charAt(i)))) == 0 ){ return false ; } } return true ; } private static int computeLetterBit (char ch) { if (ch >= 'A' && ch <= 'Z' ){ return (ch-'A' ); } if (ch >='a' && ch <= 'z' ){ return (ch-'a' +26 ); } throw new IllegalArgumentException("参数错误!非法字符!" ); } public static void main (String[] args) throws Exception { String a = "AcBbDg" ; String b = "AcD" ; String c = "DdD" ; String d ="*AcN" ; if (isContainString(a,b)){ System.out.println("a 包含b所有的字符!!" ); }else { System.out.println("a 不包含b所有的字符!!!" ); } if (isContainString(a,c)){ System.out.println("a 包含c所有的字符!!" ); }else { System.out.println("a 不包含c所有的字符!!" ); } if (isContainString(a,d)){ System.out.println("a 包含d所有的字符!" ); } } }
背景 回文,英文palindrome,指一个顺着读和反过来读都一样的字符串,比如madam、我爱我,这样的短句在智力性、趣味性和艺术性上都颇有特色,中国历史上还有很多有趣的回文诗。
那么,我们的第一个问题就是:判断一个字串是否是回文?
Java代码 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 public class PalindromeString { public static boolean isPalindrome (final char [] palindromeString,final int len) { if ( null == palindromeString || len <1 ){ return false ; } int middle = middlePos(len); int before = middle -1 ; int behind = (len % 2 == 0 ) ? middle : (middle + 1 ); while (before >= 0 ){ System.out.println(palindromeString[before]+"----" +palindromeString[behind]); if (palindromeString[before--] != palindromeString[behind++]){ return false ; } } return true ; } private static int middlePos (final int num ) { int middle = (num >> 1 ); return middle > 0 ? middle : 0 ; } public static void main (String[] args) {P char [] palindromeString1 = {'a' ,'b' ,'c' ,'c' ,'b' ,'a' }; char [] palindromeString2 = {'a' ,'b' ,'c' ,'b' ,'d' }; if (isPalindrome(palindromeString1,6 )){ System.out.println("palindromeString1 is PalindromeString" ); }else { System.out.println("palindromeString1 is not PalindromeString" ); } if (isPalindrome(palindromeString2,5 )){ System.out.println("palindromeString2 is PalindromeString" ); }else { System.out.println("palindromeString2 is not PalindromeString" ); } } }
背景 上一小节已经介绍了回文字符串,不再做介绍。最长回文子串,就是:给定一个字符串,求它的最长回文串的长度,并且把这个回文子串打印出来。
算法分析 由于查找最长回文子串问题的解决方法有很多种,而最快捷高效的算法,理解起来比较复杂,这里介绍一下,所谓的时间空间复杂度都是O(N)的Manacher
算法.
Manacher
算法,首先会初始化处理字符串。在上一节的时候我们会需要针对奇数偶数不同情况来不同处理,但是如果按照Manacher
算法的初始化规则,就可以统一为奇数一种情况来处理,方便快捷。
初始化规则就是:在字符串中每个字符前后都是用#
来分隔,包括开头和结尾;然后再新字符串的前后分别添加$
和^
符号标识字符串开始和结束。例如给定一个字符串abcdcda
,初始化处理之后为$#a#b#c#d#c#d#a#^
接下来,就需要对每一个字符分别计算它对应的最大回文子串长度(包括字符本身),例如,
S[i]
`$#a#b#c#d#c#d#a#^`
P[i]
`01212121414121210`
这样子计算的P[i]对应的值和初始化前字符的回文长度关系是:old[i] = P[i]-1
.
*重点来了,如果分别计算每一个长度,那么其实效率并不高,那么有没有什么特征能快速计算每个字符对应的最长回文长度呢?Manacher
算法告诉我们是有的。
假设:id是当前求得的最长回文子串中心的位置,mx为当前最长回文子串的右边界(回文子串不包括该右边界),即mx = id + P[id]。记j = id - (i-id) = 2*id – i ,即 j 是 i 关于 id 的对称点。
当i <
mx 时;存在一个非常神奇的结论P[i] >= min(P[2*id - i], mx - i),证明略。
当i >= mx, 无法对P[i]做更多的假设,只能P[i] = 1,然后按照原始方法再去匹配计算长度。