算法知识梳理(2) 字符串算法第一部分

1、概要

本文介绍了有关字符串的算法第一部分的Java代码实现,全部代码都可经过 在线编译器 直接运行,算法目录:java

  • 替换字符串中的空格
  • 输入一个字符串,打印出该字符串的全部排列
  • 第一个只出现一次的字符
  • 翻转句子
  • 计算字符串之间的最短距离

2、代码实现

2.1 替换字符串中的空格

问题描述

实现一个函数,将字符串p中的全部空格都替换成为指定的字符串r算法

解决思路

  • 遍历原始的字符串p,统计原先字符串中空格的个数spaceNum
  • 建立一个新的数组n,用于存放替换后的字符串。因为原先字符串中空格也占了一个位置,所以新数组n的长度为p.len + (r.len - 1) * spaceNum
  • 对于p 从后往前遍历,若是 遇到了空格,那么就将须要替换的字符串r中的字符 从后往前 填入n数组中;若是 遇到了非空格,那么就将p中的字符填入n数组中。

实现代码

class Untitled {
	
	static char[] replaceSpace(char p[], char r[], int pLen, int rLen){
		int spaceNum = 0;
		int i;
		for(i = 0; i < pLen; i++){
			if(p[i] == ' ')
				spaceNum += (rLen-1);
		}
		int nLen = pLen+spaceNum;
		char n[] = new char[nLen+1];
		i = nLen-1;
		int j = pLen-1;
		while(i >= 0 && j >= 0){
			if (p[j] == ' ') {
				for (int k = rLen-1; k >= 0; k--)
					n[i--] = r[k];
			} else {
				n[i--] = p[j];
			}
			j--;
		}
		n[nLen] = 0;
		return n;
	} 
	
	public static void main(String[] args) {
		char[] source = "I am sentence with space".toCharArray();
		char[] replace = "%20".toCharArray();
		char[] result = replaceSpace(source, replace, source.length, replace.length);
		System.out.println(result);
	}
}
复制代码

运行结果

>> I%20am%20sentence%20with%20space
复制代码

2.2 输入一个字符串,打印出该字符串的全部排列

问题描述

输入一个字符串,打印出该字符串中字符的全部排列。例如输入字符串abc,则输出由字符abc所能排列出来的全部字符串abcacbbacbcacabcba数组

解决思路

这是一个 递归问题,求一个长度为n的字符串的全排列的方法为:函数

  • n[0..n.len-1]全排列的计算方法为:将n[0]位置的字符分别和n[1..n.len-1]的每个字符串交换,n[0]和交换后的n[1..n.len - 1]的全排列进行组合。咱们将字符串{s}的全排列表示为{s},那么对于abc来讲,其全排列{abc},就等因而a + {bc}b + {ac}c + {ba}
  • 以此类推,n[1..n.len - 1]的全排列,则是将n[1]分别和n[2..n.len - 1]的每个字符串交换,再求出交换后的n[2..len - 1]的全排列,递归结束的条件为n[i..n.len - 1]只有一个字符,例如,bc的全排列为b + {c}c + {b},而{c}{b}的全排列只有一种,所以递归结束,这时候就能够打印出结果。

实现代码

class Untitled {
	
	static void permutationStr(char p[], int depth, int length){
		if (depth == length) {
			System.out.println(p);
			return;
		}
		char c;
		for (int i = depth; i < length; i++){
			c = p[depth]; p[depth] = p[i]; p[i] = c;
			permutationStr(p, depth+1, length);
			c = p[depth]; p[depth] = p[i]; p[i] = c;
		}
	}
	
	public static void main(String[] args) {
		char[] source = "abc".toCharArray();
		permutationStr(source, 0, source.length);
	}
}
复制代码

运行结果

>> abc
>> acb
>> bac
>> bca
>> cba
>> cab
复制代码

2.3 第一个只出现一次的字符

问题描述

在字符串中找出第一个只出现一次的字符。如输入abaccdeff,则输出b,要求时间复杂度为O(n)spa

解决思路

这里须要采用 以空间换时间 的思想,也就是建立一个足够大的数组c,这里为256,而后对原始的数组p进行两次遍历:code

  • 第一次 从头开始 遍历p,以p的值做为数组c的下标,并将c中对应位置的值加1,也就是说c[Integer.valueOf(i)]的值表示的是字符ip中出现的次数。这和HashMap的原理有些相似,只不过是将查找的key值直接简化成为了value的整型值。
  • 第二次 从头开始 遍历p,查找数组c对应位置该值是否为1,若是为1,那么就表示它是第一次只出现一次的字符。

实现代码

class Untitled {
	
	static char firstNotRepeat(char p[], int len){
		if (len == 1) 
			return p[0];
		int c[] = new int[256];
		int i;
		char r = p[0];
		for (i = 0; i < 256; i++)
			c[i] = 0;
		for (i = 0; i < len; i++)
			c[p[i]] += 1;
		for (i = 0; i < len; i++) {
			if (c[p[i]] == 1) {
				r = p[i];
				break;
			}
		}
		return r;
	} 
	
	public static void main(String[] args) {
		char[] source = "abaccdeff".toCharArray();
		char c = firstNotRepeat(source, source.length);
		System.out.println(c);
	}
}
复制代码

运行结果

>> b
复制代码

2.4 翻转句子

问题描述

翻转句子中单词的顺序,但单词内字符的顺序不变,句子中单词以空格符隔开。例如I am a original string翻转后的结果为string original a am I递归

解决思路

实现过程分为两步:字符串

  • 第一步,将整个句子中的全部字符都翻转
  • 第二步,遍历翻转后的句子,对于句子内的每个单词,将其字符再翻转一次,就能保证单词内字符的顺序不变。翻转单词的时候,经过pStartpEnd记录每次遇到单词的起止下标,并使用子方法reverseSub对单词中的字符进行翻转。

实现代码

class Untitled {
	
	static void reverseSub(char p[], int start, int end){
		char c;
		int i = start;
		int j = end;
		while(i < j){
			c = p[i]; p[i] = p[j]; p[j] = c;
			i++; j--;
		}
	}

	static void reverseSentence(char p[], int length){
		//首先翻转整个具体的全部字符。
		reverseSub(p, 0, length-1);
		int pStart = 0;
		int pEnd = 0;
		//从头开始遍历,寻找句子中的单词,pStart和pEnd分别表示单词的起止下标。
		while(pStart < length && pEnd < length){
			if(p[pStart] == ' '){
				pStart++;
				pEnd++;
			} else if (p[pEnd] == ' ' || p[pEnd] == '\0') {
				//翻转单词中的字符。
				reverseSub(p, pStart, --pEnd);
				pStart = ++pEnd;
			} else {
				pEnd++;
			}
		}
	} 
	
	public static void main(String[] args) {
		char[] source = "I am a original string".toCharArray();
		System.out.println(source);
		reverseSentence(source, source.length);
		System.out.println(source);
	}
}
复制代码

运行结果为:get

>> string original a am I
复制代码

2.5 计算字符串之间的最短距离

问题描述

假设咱们有两个字符串AB,那么若是想要将字符串A经过如下三种操做变换成B:删除、新增和修改,操做步骤的次数就称为 字符串 A 和 B 之间的距离编译器

如今给定两个字符串,求这两个字符串之间的最短距离。

解决思路

首先,咱们须要先明确一个前提条件:若是A的长度为0,那么AB之间的距离就为B的长度,反之对于B也如此。

下面,咱们在来看普通的状况,假如A[0]B[0]相同,那么AB之间的距离就为A[1..A.len-1]B[1..B.len-1]之间的距离;假如A[0]B[0]不相同,那么想要让AB相同,执行的操做有如下几种:

  • 删除A的第一个字符,而后计算A[1..A.len-1]B[0..B.len-1]的距离
  • 删除B的第一个字符,而后计算A[0..A.len-1]B[1..B.len-1]的距离
  • 修改A的第一个字符为B的第一个字符,而后计算A[1..A.len-1]B[1..B.len-1]的距离
  • 修改B的第一个字符为A的第一个字符,而后计算A[1..A.len-1]B[1..B.len-1]的距离
  • 增长A的第一个字符到B第一个字符以前,而后计算A[1..A.len-1]B[0...B.len-1]的距离
  • 增长B的第一个字符到A第一个字符以前,而后计算A[0...A,len-1]B[1..B.len-1]的距离

对于以上这六种状况,其实最终均可以概括为 通过一次操做,再加上剩下部分的操做次数,那么咱们的接下来的工做就是 求出剩下部分的操做部分的最小值。对于上面的任意一种状况,通过划分后AB的长度都会减小,那么最终必然会达到咱们在一开始谈到的 前提条件:若是A的长度为0,那么AB之间的距离就为B的长度,反之对于B也如此。

实现代码

class Untitled {
	
	static int minValue(int t1, int t2, int t3){
		if (t1 < t2) {
			return t1 < t3 ? t1 : t3;
		} else {
			return t2 < t3 ? t2 : t3;
		}
	}

	static int calStringDis(char p1[], char p2[], int p1Start, int p2Start, int p1Len, int p2Len){
		if (p1Len == 0) {
			if (p2Len == 0)
				return 0;
			else
				return p2Len;
		}
		if (p2Len == 0) {
			if (p1Len == 0)
				return 0;
			else
				return p1Len;
		}
		if (p1[p1Start] == p2[p2Start])
			//A和B的第一个字符相同。
			return calStringDis(p1, p2, p1Start+1, p2Start+1, p1Len-1, p2Len-1);
		else {
			//(1) 删除B的第一个字符,或者将B的第一个字符放到A以前。
			int t1 = calStringDis(p1, p2, p1Start, p2Start+1, p1Len, p2Len-1);
			//(2) 删除A的第一个字符,或者将A的第一个字符放到B以前。
			int t2 = calStringDis(p1, p2, p1Start+1, p2Start, p1Len-1, p2Len);
			//(3) 修改A的第一个字符为B的第一个字符,或者修改B的第一个字符为A的第一个字符。
			int t3 = calStringDis(p1, p2, p1Start+1, p2Start+1, p1Len-1, p2Len-1);
			//计算以上三种状况的最小值,再加上此次操做的次数。
			return minValue(t1, t2, t3) + 1;
		}
	} 
	
	public static void main(String[] args) {
		char[] source = "abcde".toCharArray();
		char[] other = "bcd".toCharArray();
		System.out.println("" + calStringDis(source, other, 0, 0, source.length, other.length));
	}
}
复制代码

运行结果

>> 2
复制代码

更多文章,欢迎访问个人 Android 知识梳理系列:

相关文章
相关标签/搜索