Java算法面试题(003) 如何检查一个字符串是另外一个字符串的旋转字符串

声明:本文为本博主翻译,未经允许,严禁转载!

简介

编写一个程序来检查一个字符串是否是另一个字符串的旋转字符串是一个常见的编码问题,你将在编程工作面试中看到。一个字符串被认为是另一个字符串的旋转,如果它具有相同的长度,包含相同的字符,并且围绕其中一个字符旋转。例如,字符串“bcda”是“abcd”的旋转,但“bdca”不是字符串“abcd”的旋转。对这个有趣的问题最简单的解决方案之一是首先检查两个字符串是否具有相同的长度,如果不是一个字符串不能是另一个字符串的旋转。如果它们的长度相同,那么只需要通过连接第一个字符串和自己来创建另一个字符串,现在检查第二个字符串是否是这个连接字符串的子字符串,如果是,那么第二个字符串是第一个字符串的旋转。

您可能想知道,这个解决方案是如何工作的?那么,你可以围绕某个字符旋转字符串,如果你加入了字符串本身,那么它实际上包含了它自己的所有旋转版本。因此,当您使用contains()方法检查旋转后的字符串是此串联字符串的一部分时,如果是,则返回true,否则返回false。

让我们用一个例子来理解这个,假设“JavaProgrammer”是第一个字符串,“ProgrammerJava”是第二个字符串。你可以围绕从索引0开始的任何字符旋转字符串,即'J'到index=length-1,这是'r ”。
现在,如果将“JavaProgrammer”与自身连接起来,则会得到“JavaProgrammerJavaProgrammer”,现在您可以看到它包含第一个字符串的每个可能的旋转。这是一个非常好的解决方案,当我第一次了解它的时候,我和你现在一样惊讶。
顺便说一句,如果面试官会问你如何解决这个问题,而不使用字符串连接,那么你做什么?我会告诉你如何在这篇文章中做到这一点。

问题

给定两个字符串s1和s2,你将如何检查s1是否是s2的旋转版本?

方案

正如我所说,解决这个问题有两种方法,一是使用字符串连接,二是不使用字符串连接。

解决方案1的逻辑

下面是通过使用字符串连接来检查字符串是否是另一个字符串的旋转的步骤:
1. 使用+运算符连接两个字符串s1和s1。如果你愿意的话,你也可以使用StringBuffer或者StringBuilder,但是+看起来不错,干净,而且在内部也使用StirngBuilder(见Effective Java)。
2. 通过使用contains()方法检查连接版本中是否存在旋转版本。

解决方案2的逻辑

下面是检查一个给定的String s2是否是String s1的旋转而不使用字符串连接的步骤。
1. 检查两个字符串的长度是否相同,如果不是,则不是旋转。如果是,则继续下一步。
2. 检查两个字符串是否相等,如果是,则s2是s1的一个旋转。如果不是,则转到下一步。
3. 取第一个字符串的第一个字符,并在第二个字符串中找到索引。如果找不到,则不是旋转,但是如果找到,继续下一步。
4. 用找到的索引减去旋转字符串的长度以找到最终位置。
5. 检查被旋转的String的第一个字符是否与输入String的最后位置的字符相同,并且input.substring(finalPos)等于rotate.substring(0,index)。

如果没有限制,可以使用任何解决方案解决问题,但如果面试官不允许字符串连接,则使用第二种解决方案。

Java代码实现

/**
 * Java Program to check if one String is rotation of other. In this program, we
 * will see two solution of this interesting problem, one by using String
 * concatenation and other without using String concatenation.
 * 
 * @author Javin
 *
 */
public class RotateStringDemo {

	/**
	 * Returns true if one string is rotation of another, nulls are not
	 * considered rotation of each other
	 * 
	 * @param str
	 * @param rotated
	 * @return true if rotated is rotation of String str
	 */

	public static boolean isRotatedVersion(String str, String rotated) {
		boolean isRotated = false;

		if (str == null || rotated == null) {
			return false;
		} else if (str.length() != rotated.length()) {
			isRotated = false;
		} else {
			String concatenated = str + str;
			isRotated = concatenated.contains(rotated);
		}

		return isRotated;
	}

	/**
	 * Return true if rotated is rotation of input String
	 * 
	 * @param input
	 * @param rotated
	 * @return true if one String is rotation of other
	 */

	public static boolean isRotated(String input, String rotated) {
		if (input == null || rotated == null) {
			return false;
		} else if (input.length() != rotated.length()) {
			return false;
		}

		int index = rotated.indexOf(input.charAt(0));
		if (index > -1) {
			if (input.equalsIgnoreCase(rotated)) {
				return true;
			}

			int finalPos = rotated.length() - index;
			return rotated.charAt(0) == input.charAt(finalPos)
					&& input.substring(finalPos).equals(rotated.substring(0, index));
		}

		return false;
	}

	public static void main(String args[]) {
		String test = "abcd";
		String rotated = "dabc";
		boolean isRotated = isRotatedVersion(test, rotated);
		System.out.printf("Is '%s' is rotation of '%s' : %b %n", rotated, test, isRotated);
	}

}

Junit测试

这里是一些单元测试来验证两个版本的字符串旋转逻辑。这是使用JUnit 4库编写的,因此您需要将junit4.jar包含到您的类路径中以运行这些测试。 @Test注释用于创建测试方法,这些方法将由JUnit Runner运行。请参阅JUnit in Action以了解有关JUnit如何工作以及如何执行测试用例的更多信息。

public class RotateStringDemoTest {
	@Test
	public void testIsRotatedVersion() {
		assertTrue(isRotatedVersion("abc", "bca"));
		assertTrue(isRotatedVersion("abc", "cab"));
		assertFalse(isRotatedVersion("abc", "bac"));
		assertFalse(isRotatedVersion("abc", null));
		assertFalse(isRotatedVersion("abc", ""));
		
	}

	@Test
	public void testisRotated() {
		assertTrue(isRotated("1234", "2341"));
		assertTrue(isRotated("1234", "3412"));
		assertTrue(isRotated("1234", "4123"));
		assertFalse(isRotated("1234", "23s41"));
		assertFalse(isRotated("1234", null));
		assertFalse(isRotated("1234", ""));
	}
}

这是关于如何检查两个字符串是否在Java中相互旋转。最简单的解决方案是只连接原始和旋转的字符串,并检查旋转是否存在于大的连接字符串中。这是一个了不起的解决方案,因为当你加入原始和旋转的版本时,它包含了第一个字符串的每一个可能的旋转。如果给定的旋转出现在串联的字符串中,那么它肯定是给定字符串的旋转。

原文链接

2 Ways to check if A String is rotation of other in Java?

译者注释

方案2的处理其实还是有问题,它无法处理包含重复字符串的情况,相应的实现可以修改如下:

	public static boolean isRotated(String input, String rotated) {
		if (input == null || rotated == null) {
			return false;
		} else if (input.length() != rotated.length()) {
			return false;
		}

		int index = 0;
		int finalPos = 0;

		do {
			index = rotated.indexOf(input.charAt(0), index);
			if (index < 0) {
				return false;
			}

			// if (input.equalsIgnoreCase(rotated)) {
			// return true;
			// }

			finalPos = rotated.length() - index;
			if (rotated.charAt(0) == input.charAt(finalPos)
					&& input.substring(finalPos).equals(rotated.substring(0, index))) {
				return true;
			}
			
			index += 1;
		} while (index < input.length());

		return false;
	}

阅读更多
文章标签: Java String rotation
所属专栏: Java算法面试题
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭