黑马程序员——字符串2:练习

来源:互联网 发布:ubuntu 文件编辑器 编辑:程序博客网 时间:2024/05/22 03:37

------- android培训、java培训、期待与您交流! ----------

1. 练习一

需求:模拟trim方法,去除字符串两端的空格。

分析:

这里我们引用标准类库String类的trim方法API文档描述:

       如果此String对象表示一个空字符序列,或者此String对象表示的字符序列的第一个和最后一个字符的代码都大于'\u0020'(空格字符),则返回对此String对象的引用。

       否则,若字符串中没有代码大于'\u0020'的字符,则创建并返回一个表示空字符串的新String对象。

       否则,假定k为字符串中代码大于'\u0020'的第一个字符的索引,m为字符串中代码大于'\u0020'的最后一个字符的索引。创建一个新的String对象,它表示此字符串中从索引 k 处的字符开始,到索引m处的字符结束的子字符串,即this.substring(k, m+1)的结果。

实现方式:

返回值类型为String,传入参数为需要被操作的字符串对象——String str。

       首先,定义一个length局部整型变量,初始化值为str.length(),因为下面的代码需要反复使用字符串的长度,这样做可以减少调用length()方法的次数,提高代码效率。

       其次,通过isEmpty()方法判断该字符串是否为空,如果满足返回原字符串。

       第三,通过两次charAt方法,判断字符串第一个和最后一个字符是否是空格,如果均不是则返回原字符串。

       第四,定义表示字符串角标值的局部整型变量index。在while循环内,通过charAt方法从头开始判断该字符串是否全部由空格组成,如果是则返回一个空字符串。

       最后,如果以上判断都不满足,那么定义两个整型变量start和end,表示前后第一个非空格字符角标值。先后通过两个while循环,分别从头尾开始判断某个角标位上字符是否是空格,直到出现非空格字符,并通过start和end记录第一个非空格字符角标值,最终通过substring方法,返回用start和end截取的子串。

代码:

代码1

class StringTest{public static void main(String[] args){String str1 = "";System.out.println("空字符串:"+myTrim(str1));String str2 = "Hello World!";System.out.println("没有空格:("+myTrim(str2)+")");String str3 = "                       ";System.out.println("全为空格:(" + myTrim(str3) + ")");String str4 = "   Hello World!       ";System.out.println("前后空格:("+ myTrim(str4) + ")");}public static String myTrim(String str){//获取字符串长度int length = str.length(); //判断字符串是否为空if(str.isEmpty())return str; //判断字符串前后是否有空格if(str.charAt(0) != ' ' && str.charAt(length-1) != ' ')return str; //判断字符串是否是全部由空格组成int index = 0;while(str.charAt(index) == ' '){//当判断到最后一个角标位仍为空格,返回一个空字符串if(index == length-1)return "";index++;} //记录前后两个角标位数值int start = 0, end = str.length()-1;             //从头角标位开始判断是否是空格,同时判断两角标值是否相同while(str.charAt(start) == ' ')start++;             //从尾角标位开始判断是否是空格,同时判断两角标值是否相同while(str.charAt(end) == ' ')end--;return str.substring(start,end+1);}}
运行结果为:

空字符串:

没有空格:(Hello World!)

全为空格: ()

前后空格:(Hello World!)

另外,有兴趣的朋友可以查看String类trim方法的源代码。

2. 练习二

需求一:将字符串反转。

分析

首先,将字符串转换为字符数组。

其次,将字符数组反转。

最后,将反转后的字符数组转换为字符串。

实现方式:

实现方式比较简单,不再赘述。

代码:

代码2:

class StringTest2{public static void main(String[] args){String oldStr = "123456789";String newStr = reverseString(oldStr);System.out.println(oldStr);System.out.println(newStr);}public static String reverseString(String str){char[] arr = str.toCharArray();             //将前后对应角标位上的字符进行交换,将该动作单独封装为一个方法reverse(arr); return new String(arr);}private static void reverse(char[] arr){for(int start = 0, end = arr.length-1; start <= end; start++, end--)//将交换动作再单独封装为一个方法swap(arr,start, end);}private static void swap(char[] arr, int x, int y){char temp = arr[x];arr[x] = arr[y];arr[y] = temp;}}
运行结果为:

123456789

987654321

        针对上述例子,给大家一个建议:上例中我们将一个“大”的功能细分为多个小方法,而不是“挤”在一个函数中,这样做不仅提高了代码的阅读性、后期的维护性,而且还提高了放法的复用性,其他方法也都可以使用这些小方法,而不必重复定义。

需求二:对某个字符串的指定部分字符进行反转。

分析:

首先,获取原字符串的字符数组形式。

其次,将上述字符数组指定角标位之间的字符进行反转。

最后,将反转后的字符数组转换为字符串返回。

实现方式:

只需在代码2的基础上进行修改即可。

首先,定义一个reverseString(Stringstr)方法的重载方法,参数列表为String str, int start, int end,其中start和end表示需要反转的部分字符串头尾角标值。这样就表示调用多参数reverseString方法,用于反转部分字符串;调用单参数reverseString方法,反转整个字符串。

其次,将代码2中的reverse方法参数列表改为char[]arr, int x, int y,x和y分别表示,需要反转的部分字符串(亦可理解为字符数组)的头尾角标值。相对应的for循环中start和end变量的初始化值不再是0和arr.length-1,而是x和y-1。这里之所以定义为y-1,是为了贯彻前述包含头,不包含尾的思想,那么相应的调用该方法时,传入的尾部角标值应比实际值大1。

这样一来,如果想要反转整个字符串,只需调用reverseString的重载方法,并传入头角标值0和字符串长度即可,无需重复定义方法体。

其他部分不做修改。

代码:

代码3:

class StringTest3{public static void main(String[] args){String oldStr = "123456789";//全部反转String newStr = reverseString(oldStr);//部分反转String newStr2 = reverseString(oldStr, 2, 4);System.out.println(oldStr);System.out.println(newStr);System.out.println(newStr2);}//该方法则专门用于反转整个字符窜public static String reverseString(String str){//只需调用reverseString重载方法,并传入头角标值和字符串长度即可reverseString(arr,0, str.length());}//定义reverseString重载方法,用于反转部分字符串public static String reverseString(String str, int start, int end){char[] arr = str.toCharArray(); //传入需要反转部分的字符串头尾角标值reverse(arr,start, end); return new String(arr);}private static void reverse(char[] arr, int x, int y){//start和end初值为需要反转部分的字符串头尾角标值for(int start = x, end = y-1; start<=end; start++, end--)swap(arr,start, end);}private static void swap(char[] arr, int x, int y){char temp = arr[x];arr[x] = arr[y];arr[y] = temp;}}
运行结果为:

123456789

987654321

124356789

3. 练习三

需求:

获取一个字符串在另一个字符串中出现的次数。

分析一:

首先要判断目标字符串中是否包含指定子串,如果不包含直接返回0。

其次,定义一个计数器。

第三,循环获取子串在目标字符串中的位置,获取到一个,计数器就记一次数。直到获取到的子串位置角标为-1时停止循环,并返回计数器数值。

实现方式一:

       定义三个局部整型变量count、index和subLength,分别表示子串个数,起始搜索角标值以及子串长度。子串长度通过length()方法获取,可以避免后续代码频繁调用该方法。开启while循环,结束条件是从index角标位开始搜索到的子串所在角标是否为-1。如果不为-1,count自增,并将子串的长度相加至index值,表示下次从子串后面的位置开始搜索。最后返回子串个数——count。

代码:

代码5:

class StringTest5{public static void main(String[] args){String target = "Hello World! Hello Java!";String sub = "Hello";System.out.println(getSubCount(target,sub));}public staticintgetSubCount(String target, String sub){int count = 0, index = 0, subLength = sub.length();//判断从指定位置开始搜索获取的子串角标值是否为-1while((index = target.indexOf(sub, index)) != -1){count++;//表示下次从子串后面的位置开始搜索index += subLength;} return count;}}
运行结果为:

2

分析二:

通过split方法,按照子串将原字符串分割成字符串数组,再对字符数组的长度进行一定的处理,就可以作为子串的个数返回。

实现方式二:

split方法的特点是,如果在原字符串头部就包含一个子串,那么就会分割出一个空字符串,这就是需要对字符数组的长度进行处理的原因。这里我们以“ABC”和“22”字符串的组合为例进行简单分析。其中“22”为分割条件子串。组合方式有以下四种,

"ABC22ABC22ABC":子串个数2,字符数组长度3

"22ABC22ABC22ABC":子串个数3,字符数组长度4

"ABC22ABC22ABC22":子串个数3,字符数组长度3

"22ABC22ABC22ABC22":子串个数4,字符数组长度4

        那么通过上述分析得知,只有在原字符串末尾不包含子串的情况下,子串个数比分割获得的字符数组长度少1,因此只需要通过lastIndexOf方法判断最后一个子串是否在原字符串的尾部即可。

代码:

代码6:

class StringTest6{public static void main(String[] args){String str1 = "ABC22ABC22ABC";String str2 = "ABC22ABC22ABC22";String str3 = "22ABC22ABC22ABC";String str4 = "22ABC22ABC22ABC22";String sub = "22"; System.out.println(getSubCount(str1,sub));System.out.println(getSubCount(str2,sub));System.out.println(getSubCount(str3,sub));System.out.println(getSubCount(str4,sub));}public static int getSubCount(String source, String sub){/*如果最后一个子串在原字符串的末尾,那么其起始角标值就是source.length()-sub.length(),大家可以自行验证。如果满足,直接返回字符数组长度即可,如果不满足,返回值应是字符数组长度减1*/return source.lastIndexOf(sub)!= source.length()-sub.length() ? source.split(sub).length-1 : source.split(sub).length;}}
运行结果为:

2

3

3

4

补充:

       还有一种实现方法与方法一类似,不同之处在于while循环的判断条件内通过indexOf(Stringstr)方法获取角标值,为了避免重复获取角标值,在循环内通过substring方法传入index值截取原字符串,不断去掉已经获取到的子串以及子串前的字符,这样也可以获取子串个数,但是会在内存中产生大量字符串对象,不利于内存优化。

4. 练习四

需求:

获取两个字符串中最长相同子串。

分析:

       假设有a、b一短一长两个字符串(如果长度相同,任取一个字符串为短串)。首先判断b中是否包含a,如果包含,那么a就是最长相同子串,否则从a中截取长度减1的子串,有两种截取方式——0角标到倒数第二个角标,以及1角标到末尾角标。再分别判断这两个子串是否包含于b中,以此类推,不断缩短a串的长度,这样可以保证取到某个长度下的所有子串,并逐一判断是否包含在b中。

实现方式:

首先,要判断两个字符串的长度,递减截取短串会比较高效。

其次,由于从短串截取的子串长度会不断变小,因此需要定义一个for循环,循环内的变量x不断递增,其中x表示短串长度的递减量。

第三,在外层for循环内再嵌套一个for循环,不断地从原短串中,通过两个不断自增的变量——start和end——截取子串。start表示头角标值,初始值为0;end表示尾角标值,初始化值由短串长度减去外层for循环中的递增变量算得。由于头尾角标值不断自增,这样就可以截取到某个长度下的所有子串,并逐一判断这些子串是否包含在长串中。

代码:

代码7:

class StringTest7{public static void main(String[] args){String str1 = "abcdefJavafedcba";String str2 = "123Java321"; System.out.println(getMaxSub(str1,str2));}public static String getMaxSub(String s, String l){/*先判断两字符串的长短,为便于代码阅读要求s表示短字符串,l表示长字符串如果不满足上述要求,就交换两个字符串*/if(s.length() > l.length()){String temp = l;l = s;s = temp;} int slength = s.length();String temp = null;//第一层循环决定短串递减量,每次递减一个长度for(int x = 0; x <slength; x++){//第二层循环按照第一层循环的递减量,按顺序截取子串for(int start=0, end=slength-x ;end<=slength ; start++, end++){//temp表示截取的子串temp = s.substring(start, end);//为方便观察代码的运作规律,打印每次截取的子串System.out.println(temp);                           //如果长串中包含截取得到的子串,表示找到最长相同子串,返回该子传if(l.contains(temp))//这里也可以通过indexOf判断return temp;}} //如果最终未能找到最长相同子串,返回空字符串return "";}}
运行结果为:

123Java321

123Java32

23Java321

123Java3

23Java32

3Java321

123Java

23Java3

3Java32

Java321

123Jav

23Java

3Java3

Java32

ava321

123Ja

23Jav

3Java

Java3

ava32

va321

123J

23Ja

3Jav

Java

Java

0 0
原创粉丝点击