796. Rotate String
題目 / Problem
中文:
給定兩個字串 s 和 goal,如果 s 經過若干次「移位」操作後可以變成 goal,則回傳 true,否則回傳 false。
一次「移位」操作:把 s 最左邊的字元搬到最右邊。
例如 s = "abcde",經過一次移位後變成 "bcdea"。
English:
Given two strings s and goal, return true if and only if s can become goal after some number of shifts. A shift moves the leftmost character of s to the rightmost position. For example, "abcde" becomes "bcdea" after one shift.
Constraints / 限制:
- 1 <= s.length, goal.length <= 100
- s 和 goal 只包含小寫英文字母 / both consist of lowercase English letters.
Example / 範例:
- Input: s = "abcde", goal = "cdeab" → Output: true (shift 兩次:abcde → bcdea → cdeab)
- Input: s = "abcde", goal = "abced" → Output: false
名詞解釋 / Glossary
- Rotation / 旋轉(循環移位): 把字串前面的一段搬到後面。
"abcde"的所有旋轉結果是"abcde"、"bcdea"、"cdeab"、"deabc"、"eabcd",共n種。 - Concatenation / 字串串接: 把兩個字串接在一起。例如
"abcde" + "abcde" = "abcdeabcde"。在 C 裡需要手動配置記憶體並用strcpy/strcat;在 C++ 直接用+即可。 - Substring search / 子字串搜尋: 在一個大字串裡找另一個小字串是否出現過。C 用
strstr,C++ 用string::find。 - Key insight / 關鍵觀察:
s的所有旋轉結果都是s + s的子字串。例如s = "abcde",則s+s = "abcdeabcde",而"cdeab"確實出現在裡面。這把「枚舉旋轉」變成「一次子字串搜尋」。 strstr(C):strstr(haystack, needle)在haystack中搜尋needle,找到則回傳指向匹配位置的指標,找不到則回傳NULL。std::string::find(C++): 成員函式,回傳第一次出現的索引;找不到則回傳特殊常數string::npos。
思路
最直覺的暴力法是模擬:對 s 進行 n 次旋轉,每次比較是否等於 goal。但這需要實際搬動字元,寫起來繁瑣。更聰明的觀察是:s 的所有旋轉結果,都會以子字串的形式藏在 s + s 裡。原因很簡單,假設旋轉了 k 次,結果就是 s[k..n-1] + s[0..k-1],而這正好對應 s + s 從第 k 位開始長度為 n 的那一段。所以演算法只剩兩件事:先檢查兩字串長度是否相等(不等就直接 false,因為旋轉不改變長度),再檢查 goal 是否為 s + s 的子字串。一個關鍵不變量(invariant)是「旋轉保留長度與字元多重集」,長度檢查保證我們不會誤判類似 "ab" 與 "aab" 的情況。
The brute-force approach simulates: rotate s up to n times and compare with goal each time. That works but is fiddly. A cleaner observation: every rotation of s is a contiguous substring of s + s. If we rotate k times we get s[k..n-1] + s[0..k-1], which is exactly the length-n window in s + s starting at index k. So the algorithm reduces to two checks: lengths must match (rotation preserves length, so unequal lengths immediately give false), and goal must appear as a substring of s + s. The length guard is what stops false positives like s = "ab", goal = "aab" — without it, "aab" would be found inside "abab" even though the strings have different sizes.
逐步走查 / Walkthrough
Example: s = "abcde", goal = "cdeab".
| Step / 步驟 | Action / 動作 | Value / 值 |
|---|---|---|
| 1 | Compare lengths / 比較長度 | len(s) = 5, len(goal) = 5 ✓ |
| 2 | Build s + s / 串接 |
"abcdeabcde" |
| 3 | Search goal in s+s / 在串接結果中搜尋 goal |
Look for "cdeab" |
| 4 | Match found at index 2 / 在索引 2 處找到匹配 | "ab[cdeab]cde" |
| 5 | Return / 回傳 | true |
對照失敗例 / Contrast with the failing case: s = "abcde", goal = "abced". s+s = "abcdeabcde",在裡面找 "abced" 找不到 → 回傳 false。
Solution — C
// Algorithm / 演算法:
// 1. 若長度不同直接回傳 false / different lengths → false.
// 2. 把 s 串接自己得到 s+s / concatenate s with itself.
// 3. 用 strstr 在 s+s 裡找 goal,找到即為某種旋轉 / strstr finds goal as a substring iff goal is a rotation of s.
#include <stdbool.h> // 提供 bool / true / false 型別 / provides the bool type
#include <string.h> // 提供 strlen / strcpy / strcat / strstr / for string utilities
#include <stdlib.h> // 提供 malloc / free / for dynamic memory
bool rotateString(char* s, char* goal) {
size_t n = strlen(s); // n = s 的長度 / length of s
size_t m = strlen(goal); // m = goal 的長度 / length of goal
if (n != m) return false; // 長度不同必不可能 / different lengths cannot be rotations
// 配置 2n+1 個 char 的空間:2n 用來放 s+s,+1 留給結尾的 '