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

------- 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();
		//判断从指定位置开始搜索获取的子串角标值是否为-1
		while((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

内容概要:本文介绍了ENVI Deep Learning V1.0的操作教程,重点讲解了如何利用ENVI软件进行深度学习模型的训练与应用,以实现遥感图像中特定目标(如集装箱)的自动提取。教程涵盖了从数据准备、标签图像创建、模型初始化与训练,到执行分类及结果优化的完整流程,并介绍了精度评价与通过ENVI Modeler实现一键化建模的方法。系统基于TensorFlow框架,采用ENVINet5(U-Net变体)架构,支持通过点、线、面ROI或分类图生成标签数据,适用于多/高光谱影像的单一类别特征提取。; 适合人群:具备遥感图像处理基础,熟悉ENVI软件操作,从事地理信息、测绘、环境监测等相关领域的技术人员或研究人员,尤其是希望将深度学习技术应用于遥感目标识别的初学者与实践者。; 使用场景及目标:①在遥感影像中自动识别和提取特定地物目标(如车辆、建筑、道路、集装箱等);②掌握ENVI环境下深度学习模型的训练流程与关键参数设置(如Patch Size、Epochs、Class Weight等);③通过模型调优与结果反馈提升分类精度,实现高效自动化信息提取。; 阅读建议:建议结合实际遥感项目边学边练,重点关注标签数据制作、模型参数配置与结果后处理环节,充分利用ENVI Modeler进行自动化建模与参数优化,同时注意软硬件环境(特别是NVIDIA GPU)的配置要求以保障训练效率。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值