Java演算法面試題(003) 如何檢查一個字串是另外一個字串的旋轉字串

劉近光發表於2017-11-22

宣告:本文為本博主翻譯,未經允許,嚴禁轉載!

簡介

編寫一個程式來檢查一個字串是否是另一個字串的旋轉字串是一個常見的編碼問題,你將在程式設計工作面試中看到。一個字串被認為是另一個字串的旋轉,如果它具有相同的長度,包含相同的字元,並且圍繞其中一個字元旋轉。例如,字串“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;
	}

相關文章