[关闭]
@xujun94 2016-09-21T11:51:40.000000Z 字数 7694 阅读 1651

笔试题—字符串常见的算法题集锦

本篇博客主要讲解一下四个问题

转载请注明原博客地址: http://blog.csdn.net/gdutxiaoxu/article/details/52602327

例子源码下载地址: http://download.csdn.net/detail/gdutxiaoxu/9635393

KMP算法

关于KMP算法的分析,这里就不讲解了,有兴趣的可以参考这篇博客:从头到尾彻底理解KMP

代码如下

  1. package com.xujun.stringfind;
  2. public class KMPFind {
  3. public static void main(String[] args) {
  4. String s = "abcbb2abcabx";
  5. String c = "abca";
  6. int[] next = new int[s.length() + 1];
  7. next = getNext(c);
  8. for (int i = 0; i < next.length; i++) {
  9. System.out.println("=" + next[i]);
  10. }
  11. int find = matchNext(s, c, 0);
  12. System.out.println("find=" + find);
  13. int[] nextVal = getNextVal(c);
  14. for (int i = 0; i < nextVal.length; i++) {
  15. System.out.println("=" + nextVal[i]);
  16. }
  17. int matchNextVal = matchNextVal(s, c, 0);
  18. System.out.println("matchNextVal=" + matchNextVal);
  19. }
  20. /**
  21. * 注意这里为了保持保持一致性 ,第一个next[0]没有用到
  22. *
  23. * @param c
  24. * @return
  25. */
  26. private static int[] getNextVal(String c) {
  27. int[] nextVal = new int[c.length() + 1];
  28. int front = 0;
  29. int behind = 1;
  30. nextVal[1] = 0;
  31. /**
  32. * c.charAt(front-1)表示前缀字符 ,c.charAt(behind-1)表示后缀字符
  33. */
  34. while (behind < c.length()) {
  35. if (front == 0 || c.charAt(front - 1) == c.charAt(behind - 1)) {
  36. ++front;
  37. ++behind;
  38. if (c.charAt(front - 1) != c.charAt(behind - 1)) {
  39. nextVal[behind] = front;
  40. } else {
  41. nextVal[behind] = nextVal[front];
  42. }
  43. } else {
  44. // 前缀索引回溯
  45. front = nextVal[front];
  46. }
  47. }
  48. return nextVal;
  49. }
  50. /**
  51. * 注意这里为了保持保持一致性 ,第一个next[0]没有用到
  52. *
  53. * @param c
  54. * @return
  55. */
  56. private static int[] getNext(String c) {
  57. int[] next = new int[c.length() + 1];
  58. int front = 0;
  59. int behind = 1;
  60. next[1] = 0;
  61. /**
  62. * c.charAt(front-1)表示前缀字符 c.charAt(behind-1)表示后缀字符
  63. */
  64. while (behind < c.length()) {
  65. if (front == 0 || c.charAt(front - 1) == c.charAt(behind - 1)) {
  66. ++front;
  67. ++behind;
  68. next[behind] = front;
  69. } else {
  70. // 前缀 索引回溯
  71. front = next[front];
  72. }
  73. }
  74. return next;
  75. }
  76. public static int matchNextVal(String source, String c, int pos) {
  77. int i;
  78. int[] nextVal = getNextVal(c);
  79. if (pos < 1) {
  80. i = 1;
  81. } else {
  82. i = pos + 1;
  83. }
  84. int j = 1; // i控制S,j控制T;
  85. while (i <= source.length() && j <= c.length()) {
  86. if (j == 0 || source.charAt(i - 1) == c.charAt(j - 1)) {
  87. ++i;
  88. ++j;
  89. } else {
  90. j = nextVal[j]; // j退回合适的位置,i值不变
  91. }
  92. }
  93. if (j > c.length())
  94. return i - c.length() - 1;
  95. else
  96. return -1;
  97. }
  98. public static int matchNext(String source, String c, int pos) {
  99. int i;
  100. int[] next = getNext(c);
  101. if (pos < 1) {
  102. i = 1;
  103. } else {
  104. i = pos + 1;
  105. }
  106. int j = 1; // i控制S,j控制T;
  107. while (i <= source.length() && j <= c.length()) {
  108. if (j == 0 || source.charAt(i - 1) == c.charAt(j - 1)) {
  109. ++i;
  110. ++j;
  111. } else {
  112. j = next[j]; // j退回合适的位置,i值不变
  113. }
  114. }
  115. if (j > c.length())
  116. return i - c.length() - 1;
  117. else
  118. return -1;
  119. }
  120. }

字符串倒序输出,单词不倒序

题目
对字符串中的所有单词进行倒排。
说明:
1、每个单词是以26个大写或小写英文字母构成,可能含有非法字符
2、非构成单词的字符均视为单词间隔符;
3、要求倒排后的单词间隔符以一个空格表示;如果原字符串中相邻单词间有多个间隔符时,倒排转换后也只允许出现一个空格间隔符;
4、每个单词最长20个字母;

第一种方法

思路解析

  1. public class ReverseStr2 {
  2. public static void main(String args[]) {
  3. Scanner sc = new Scanner(System.in);
  4. while (sc.hasNext()) {
  5. String str = sc.nextLine();
  6. String[] strArray = str.split("[^a-zA-Z]+");
  7. for (int i = strArray.length - 1; i >= 2; i--) {
  8. System.out.print(strArray[i] + ' ');
  9. }
  10. // 如果字符串数组的第一个元素是空串,那么下标为1的元素就是最后一个要输出的元素,末尾不要再加空格
  11. if (strArray[0].length() == 0)
  12. System.out.println(strArray[1]);
  13. else
  14. System.out.println(strArray[1] + ' ' + strArray[0]);
  15. }
  16. }
  17. }

第二种方法

思路解析

代码如下

  1. /**
  2. * Created by xujun on 2016/9/20
  3. */
  4. public class ReverseStr {
  5. public static void main(String[] args) {
  6. Scanner sc = new Scanner(System.in);
  7. while (sc.hasNext()) {
  8. StringBuilder sb = new StringBuilder();
  9. String[] a = filter(sc.nextLine()).split(" ");
  10. sb.append(a[a.length - 1]);
  11. for (int i = a.length - 2; i >= 0; i--) {
  12. sb.append(" " + a[i]);
  13. }
  14. System.out.println(sb.toString().trim());
  15. }
  16. }
  17. public static String filter(String s) {
  18. char[] c = s.toCharArray();
  19. StringBuilder sb = new StringBuilder();
  20. boolean isFirstSpace=true;
  21. for (int i = 0; i < c.length; i++) {
  22. if ((c[i] >= 'a' && c[i] <= 'z') || (c[i] >= 'A' && c[i] <= 'Z')) {
  23. sb.append(c[i]);
  24. isFirstSpace=true;
  25. continue;
  26. }
  27. if(isFirstSpace){
  28. sb.append(' ');
  29. isFirstSpace=false;
  30. }
  31. }
  32. return sb.toString();
  33. }
  34. }

字符串全排列

思路分析

可以采用递归的形式

  1. public class permutate {
  2. public static int total = 0;
  3. public static void swap(String[] str, int i, int j)
  4. {
  5. String temp = new String();
  6. temp = str[i];
  7. str[i] = str[j];
  8. str[j] = temp;
  9. }
  10. public static void arrange (String[] str, int st, int len)
  11. {
  12. if (st == len - 1)
  13. {
  14. for (int i = 0; i < len; i ++)
  15. {
  16. System.out.print(str[i]+ " ");
  17. }
  18. System.out.println();
  19. total++;
  20. }
  21. else
  22. {
  23. for (int i = st; i < len; i ++)
  24. {
  25. swap(str, st, i);
  26. arrange(str, st + 1, len);
  27. swap(str, st, i);
  28. }
  29. }
  30. }
  31. /**
  32. * @param args
  33. */
  34. public static void main(String[] args) {
  35. // TODO Auto-generated method stub
  36. String str[] = {"a","b","c"};
  37. arrange(str, 0, str.length);
  38. System.out.println(total);
  39. }
  40. }

运行以上代码,将可以看到以下输出

a b c d
a b d c
a c b d
a c d b
a d c b
a d b c
b a c d
b a d c
b c a d
b c d a
b d c a
b d a c
c b a d
c b d a
c a b d
c a d b
c d a b
c d b a
d b c a
d b a c
d c b a
d c a b
d a c b
d a b c
24


全组合

第一种方法

思路解析

基本思路:求全组合,则假设原有元素n个,则最终组合结果是2^n个。

原因是: 用位操作方法:假设元素原本有:a,b,c三个,则1表示取该元素,0表示不取。故去a则是001,取ab则是011.所以一共三位,每个位上有两个选择0,1.所以是2^n个结果。

这些结果的位图值都是0,1,2....2^n。所以可以类似全真表一样,从值0到值2^n依次输出结果:即:

000,001,010,011,100,101,110,111

对应输出组合结果为:

空,a, b ,ab,c,ac,bc,abc.

这个输出顺序刚好跟数字0~2^n结果递增顺序一样,取法的二进制数其实就是从0到2^n-1的十进制数

  1. public static void Combination( ) {
  2. /*基本思路:求全组合,则假设原有元素n个,则最终组合结果是2^n个。原因是:
  3. * 用位操作方法:假设元素原本有:a,b,c三个,则1表示取该元素,0表示不取。故去a则是001,取ab则是011.
  4. * 所以一共三位,每个位上有两个选择0,1.所以是2^n个结果。
  5. * 这些结果的位图值都是0,1,2....2^n。所以可以类似全真表一样,从值0到值2^n依次输出结果:即:
  6. * 000,001,010,011,100,101,110,111 。对应输出组合结果为:
  7. 空,a, b ,ab,c,ac,bc,abc.
  8. 这个输出顺序刚好跟数字0~2^n结果递增顺序一样
  9. 取法的二进制数其实就是从0到2^n-1的十进制数
  10. * ******************************************************************
  11. * *
  12. * */
  13. String[] str = {"a" , "b" ,"c"};
  14. int n = str.length; //元素个数。
  15. //求出位图全组合的结果个数:2^n
  16. int nbit = 1<<n; // “<<” 表示 左移:各二进位全部左移若干位,高位丢弃,低位补0。:即求出2^n=2Bit。
  17. System.out.println("全组合结果个数为:"+nbit);
  18. for(int i=0 ;i<nbit ; i++) { //结果有nbit个。输出结果从数字小到大输出:即输出0,1,2,3,....2^n。
  19. System.out.print("组合数值 "+i + " 对应编码为: ");
  20. for(int j=0; j<n ; j++) { //每个数二进制最多可以左移n次,即遍历完所有可能的变化新二进制数值了
  21. int tmp = 1<<j ;
  22. if((tmp & i)!=0) { //& 表示与。两个位都为1时,结果才为1
  23. System.out.print(str[j]);
  24. }
  25. }
  26. System.out.println();
  27. }
  28. }

第二种方法

思路解析

n个元素选m个元素的组合问题的实现. 原理如下:

从后往前选取, 选定位置i后, 再在前i-1个里面选取m-1个.
如: 1, 2, 3, 4, 5 中选取3个元素.

代码如下

  1. package com.xujun.PermutationCombinationHolder;
  2. public final class PermutationCombinationHolder {
  3. /** 数组元素的全组合 */
  4. static void combination(char[] chars) {
  5. char[] subchars = new char[chars.length]; // 存储子组合数据的数组
  6. // 全组合问题就是所有元素(记为n)中选1个元素的组合, 加上选2个元素的组合...加
  7. // 上选n个元素的组合的和
  8. for (int i = 0; i < chars.length; ++i) {
  9. final int m = i + 1;
  10. combination(chars, chars.length, m, subchars, m);
  11. }
  12. }
  13. /**
  14. * n个元素选m个元素的组合问题的实现. 原理如下: 从后往前选取, 选定位置i后, 再在前i-1个里面选取m-1个. 如: 1, 2, 3, 4,
  15. * 5 中选取3个元素. 1) 选取5后, 再在前4个里面选取2个, 而前4个里面选取2个又是一个子问题, 递归即可; 2) 如果不包含5,
  16. * 直接选定4, 那么再在前3个里面选取2个, 而前三个里面选取2个又是一个子问题, 递归即可; 3) 如果也不包含4, 直接选取3,
  17. * 那么再在前2个里面选取2个, 刚好只有两个. 纵向看, 1与2与3刚好是一个for循环, 初值为5, 终值为m. 横向看,
  18. * 该问题为一个前i-1个中选m-1的递归.
  19. */
  20. static void combination(char[] chars, int n, int m, char[] subchars,
  21. int subn) {
  22. if (m == 0) { // 出口
  23. for (int i = 0; i < subn; ++i) {
  24. System.out.print(subchars[i]);
  25. }
  26. System.out.println();
  27. } else {
  28. for (int i = n; i >= m; --i) { // 从后往前依次选定一个
  29. subchars[m - 1] = chars[i - 1]; // 选定一个后
  30. combination(chars, i - 1, m - 1, subchars, subn); // 从前i-1个里面选取m-1个进行递归
  31. }
  32. }
  33. }
  34. /** 数组元素的全排列 */
  35. static void permutation(char[] chars) {
  36. permutation(chars, 0, chars.length - 1);
  37. }
  38. /** 数组中从索引begin到索引end之间的子数组参与到全排列 */
  39. static void permutation(char[] chars, int begin, int end) {
  40. if (begin == end) { // 只剩最后一个字符时为出口
  41. for (int i = 0; i < chars.length; ++i) {
  42. System.out.print(chars[i]);
  43. }
  44. System.out.println();
  45. } else {
  46. for (int i = begin; i <= end; ++i) { // 每个字符依次固定到数组或子数组的第一个
  47. if (canSwap(chars, begin, i)) { // 去重
  48. swap(chars, begin, i); // 交换
  49. permutation(chars, begin + 1, end); // 递归求子数组的全排列
  50. swap(chars, begin, i); // 还原
  51. }
  52. }
  53. }
  54. }
  55. static void swap(char[] chars, int from, int to) {
  56. char temp = chars[from];
  57. chars[from] = chars[to];
  58. chars[to] = temp;
  59. }
  60. static boolean canSwap(char[] chars, int begin, int end) {
  61. for (int i = begin; i < end; ++i) {
  62. if (chars[i] == chars[end]) {
  63. return false;
  64. }
  65. }
  66. return true;
  67. }
  68. public static void main(String[] args) {
  69. final char[] chars = new char[] { 'a', 'b', 'c' };
  70. permutation(chars);
  71. System.out.println("===================");
  72. combination(chars);
  73. }
  74. }

题外话

转载请注明原博客地址: http://blog.csdn.net/gdutxiaoxu/article/details/52602327

例子源码下载地址: http://download.csdn.net/detail/gdutxiaoxu/9635393

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注