diff --git a/problems/3227-vowels-game-in-a-string/3227-vowels-game-in-a-string.sln b/problems/3227-vowels-game-in-a-string/3227-vowels-game-in-a-string.sln new file mode 100644 index 0000000..3213b7e --- /dev/null +++ b/problems/3227-vowels-game-in-a-string/3227-vowels-game-in-a-string.sln @@ -0,0 +1,30 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.5.2.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "csharp", "csharp\csharp.csproj", "{CF022C7B-CAF3-706D-67E1-FB93518229B7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestProject", "test\TestProject.csproj", "{C670389A-9CE7-B456-51E5-A79D56E199CF}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {CF022C7B-CAF3-706D-67E1-FB93518229B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CF022C7B-CAF3-706D-67E1-FB93518229B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CF022C7B-CAF3-706D-67E1-FB93518229B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CF022C7B-CAF3-706D-67E1-FB93518229B7}.Release|Any CPU.Build.0 = Release|Any CPU + {C670389A-9CE7-B456-51E5-A79D56E199CF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C670389A-9CE7-B456-51E5-A79D56E199CF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C670389A-9CE7-B456-51E5-A79D56E199CF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C670389A-9CE7-B456-51E5-A79D56E199CF}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {15EA9854-706B-456C-B6A2-6AF4670868DE} + EndGlobalSection +EndGlobal diff --git a/problems/3227-vowels-game-in-a-string/README.md b/problems/3227-vowels-game-in-a-string/README.md new file mode 100644 index 0000000..5aae5cc --- /dev/null +++ b/problems/3227-vowels-game-in-a-string/README.md @@ -0,0 +1,82 @@ +# [3227] Vowels Game In A String + +## 題目資訊 +- **難度**: Medium +- **標籤**: Math, String, Brainteaser, Game Theory +- **題目連結**: https://leetcode.com/problems/vowels-game-in-a-string/ +- **練習日期**: 2025-09-12 +- **目標複雜度**: 時間 O(n)、空間 O(1) + +## 題目描述 +Alice and Bob are playing a game on a string. + +You are given a string `s`, Alice and Bob will take turns playing the following game where Alice starts **first**: + +On Alice's turn, she has to remove any **non-empty** substring from `s` that contains an odd number of vowels. +On Bob's turn, he has to remove any non-empty substring from `s` that contains an even number of vowels. +The first player who cannot make a move on their turn loses the game. We assume that both Alice and Bob play optimally. + +Return `true` if Alice wins the game, and `false` otherwise. + +The English vowels are: `a`, `e`, `i`, `o`, and `u`. + +## 先備條件與限制 +- 輸入限制:n ∈ [?, ?]、值域 ∈ [?, ?] +- 回傳/輸出格式:... +- 其他:是否允許排序/就地修改 + +## 解題思路 + +### 初步分析 +- 類型:雙指針 / 滑動視窗 / 排序 / DP / 貪心 / 圖論 ... +- 關鍵觀察: +- 複雜度目標理由: + +### 解法比較 +1. 解法A(基準/暴力): + - 思路: + - 正確性: + - 複雜度:O(?) / O(?) +2. 解法B(優化): + - 思路: + - 正確性: + - 複雜度:O(?) / O(?) + +### 乾跑(Dry Run) +- 範例:... + +### 常見陷阱 +- 邊界:空/單一/極值/全相等 +- 去重:排序後跳重複、集合 +- 溢位:使用 64-bit + +## 測試案例 + +### 範例輸入輸出 +``` +Input: ... +Output: ... +Explanation: ... +``` + +### 邊界清單 +- [ ] 空陣列/空字串 +- [ ] 單一元素 / 全相同 +- [ ] 含負數/0/大數 +- [ ] 去重 +- [ ] 大資料壓力 + +## 複雜度分析 +- 最壞:時間 O(?)、空間 O(?) + +## 相關題目 / Follow-up +- + +## 學習筆記 +- 今天學到: +- 卡住與修正: +- 待優化: + +--- +**總結**:這題的核心在於 ______,適合練習 ______。 + diff --git a/problems/3227-vowels-game-in-a-string/csharp/Program.cs b/problems/3227-vowels-game-in-a-string/csharp/Program.cs new file mode 100644 index 0000000..6076c43 --- /dev/null +++ b/problems/3227-vowels-game-in-a-string/csharp/Program.cs @@ -0,0 +1,24 @@ +// LeetCode 3227: Vowels Game In A String +// 難度: Medium +// 日期: 2025-09-12 + +using System; +using System.Collections.Generic; +using System.Linq; + +public class Solution { + public bool DoesAliceWin(string s) + { + string vowels = "aeiou"; + + foreach (var character in s) + { + if (vowels.Contains(character)) + { + return true; + } + } + return false; + } +} + diff --git a/problems/3227-vowels-game-in-a-string/csharp/csharp.csproj b/problems/3227-vowels-game-in-a-string/csharp/csharp.csproj new file mode 100644 index 0000000..e8cd599 --- /dev/null +++ b/problems/3227-vowels-game-in-a-string/csharp/csharp.csproj @@ -0,0 +1,7 @@ + + + net8.0 + enable + enable + + diff --git a/problems/3227-vowels-game-in-a-string/go.mod b/problems/3227-vowels-game-in-a-string/go.mod new file mode 100644 index 0000000..5ea7f9d --- /dev/null +++ b/problems/3227-vowels-game-in-a-string/go.mod @@ -0,0 +1,4 @@ +module leetcode/3227-vowels-game-in-a-string + +go 1.18 + diff --git a/problems/3227-vowels-game-in-a-string/go/main.go b/problems/3227-vowels-game-in-a-string/go/main.go new file mode 100644 index 0000000..f6d25f1 --- /dev/null +++ b/problems/3227-vowels-game-in-a-string/go/main.go @@ -0,0 +1,19 @@ +// LeetCode 3227: Vowels Game In A String +// 難度: Medium +// 日期: 2025-09-12 + +package vowels + +import "strings" + +// DoesAliceWin returns true if the string contains any vowel. +// Game theory reduces to: Alice wins iff there exists at least one vowel. +func DoesAliceWin(s string) bool { + const vowels = "aeiou" + for _, ch := range s { + if strings.ContainsRune(vowels, ch) { + return true + } + } + return false +} diff --git a/problems/3227-vowels-game-in-a-string/test/SolutionTests.cs b/problems/3227-vowels-game-in-a-string/test/SolutionTests.cs new file mode 100644 index 0000000..a47d454 --- /dev/null +++ b/problems/3227-vowels-game-in-a-string/test/SolutionTests.cs @@ -0,0 +1,168 @@ +// LeetCode 3227: Vowels Game in a String 單元測試(xUnit) +// Problem: Alice and Bob play a game. Alice removes substrings with odd vowels, +// Bob removes substrings with even vowels. Alice wins if string has any vowels. + +using Xunit; + +public class SolutionTests +{ + private readonly Solution _s = new Solution(); + + [Fact] + public void Example1_StringWithVowels_AliceWins() + { + // Arrange + var input = "leetcode"; // contains vowels: e,e,o,e + var expected = true; // Alice wins + + // Act + var got = _s.DoesAliceWin(input); + + // Assert + Assert.Equal(expected, got); + } + + [Fact] + public void Example2_StringWithoutVowels_BobWins() + { + // Arrange + var input = "bcdf"; // no vowels + var expected = false; // Bob wins (Alice can't make first move) + + // Act + var got = _s.DoesAliceWin(input); + + // Assert + Assert.Equal(expected, got); + } + + [Fact] + public void AllVowels_AliceWins() + { + // Arrange + var input = "aeiou"; // all vowels (odd count = 5) + var expected = true; // Alice removes entire string + + // Act + var got = _s.DoesAliceWin(input); + + // Assert + Assert.Equal(expected, got); + } + + [Fact] + public void SingleVowel_AliceWins() + { + // Arrange + var input = "a"; // single vowel + var expected = true; // Alice removes it immediately + + // Act + var got = _s.DoesAliceWin(input); + + // Assert + Assert.Equal(expected, got); + } + + [Fact] + public void SingleConsonant_BobWins() + { + // Arrange + var input = "b"; // single consonant, no vowels + var expected = false; // Alice can't move + + // Act + var got = _s.DoesAliceWin(input); + + // Assert + Assert.Equal(expected, got); + } + + [Fact] + public void MixedString_EvenVowelCount_AliceWins() + { + // Arrange + var input = "programming"; // vowels: o,a,i (3 vowels, odd) + var expected = true; // Alice can remove all vowels at once + + // Act + var got = _s.DoesAliceWin(input); + + // Assert + Assert.Equal(expected, got); + } + + [Fact] + public void LongStringNoVowels_BobWins() + { + // Arrange + var input = "bcdfghjklmnpqrstvwxyz"; // all consonants + var expected = false; // No vowels = Bob wins + + // Act + var got = _s.DoesAliceWin(input); + + // Assert + Assert.Equal(expected, got); + } + + [Fact] + public void RepeatedVowels_AliceWins() + { + // Arrange + var input = "aaaa"; // 4 vowels (even count) + var expected = true; // Alice can remove 3, leaving 1 (Bob can't move) + + // Act + var got = _s.DoesAliceWin(input); + + // Assert + Assert.Equal(expected, got); + } + + [Fact] + public void EdgeCases() + { + // Single character tests + Assert.True(_s.DoesAliceWin("e")); // vowel + Assert.False(_s.DoesAliceWin("x")); // consonant + + // Two character tests + Assert.True(_s.DoesAliceWin("ab")); // has vowel 'a' + Assert.False(_s.DoesAliceWin("xy")); // no vowels + + // Vowel at different positions + Assert.True(_s.DoesAliceWin("xabc")); // vowel at start-middle + Assert.True(_s.DoesAliceWin("abcx")); // vowel in middle + Assert.True(_s.DoesAliceWin("xbca")); // vowel at end + } + + [Fact] + public void GameTheoryValidation() + { + // Test cases that validate the game theory logic: + // If total vowels = 0: Alice loses (can't make first move) + Assert.False(_s.DoesAliceWin("bcdfg")); + + // If total vowels = odd: Alice wins (removes all vowels in one move) + Assert.True(_s.DoesAliceWin("hello")); // e,o = 2 vowels (even), but Alice still wins + Assert.True(_s.DoesAliceWin("beautiful")); // e,a,u,i,u = 5 vowels (odd) + + // If total vowels = even > 0: Alice wins (leaves exactly 1 vowel for Bob) + Assert.True(_s.DoesAliceWin("code")); // o,e = 2 vowels (even) + Assert.True(_s.DoesAliceWin("education")); // e,u,a,i,o = 5 vowels, but any vowels = Alice wins + } + + [Theory] + [InlineData("", false)] // empty string - no vowels + [InlineData("aeiou", true)] // all vowels + [InlineData("bcdfg", false)] // all consonants + [InlineData("hello", true)] // mixed with vowels + [InlineData("rhythm", false)] // no vowels (y not considered vowel) + [InlineData("queue", true)] // multiple same vowels + public void ParameterizedTests(string input, bool expected) + { + // Act & Assert + Assert.Equal(expected, _s.DoesAliceWin(input)); + } +} \ No newline at end of file diff --git a/problems/3227-vowels-game-in-a-string/test/TestProject.csproj b/problems/3227-vowels-game-in-a-string/test/TestProject.csproj new file mode 100644 index 0000000..e2d8b41 --- /dev/null +++ b/problems/3227-vowels-game-in-a-string/test/TestProject.csproj @@ -0,0 +1,19 @@ + + + net8.0 + enable + false + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + diff --git a/problems/3227-vowels-game-in-a-string/test/edge_cases.md b/problems/3227-vowels-game-in-a-string/test/edge_cases.md new file mode 100644 index 0000000..a45b86a --- /dev/null +++ b/problems/3227-vowels-game-in-a-string/test/edge_cases.md @@ -0,0 +1,20 @@ +# 邊界情況清單(3227 Vowels Game In A String) + +## 需要測試的邊界 +- [ ] 空輸入 / 單一元素 +- [ ] 重複元素 / 全相同 +- [ ] 極值(最小/最大) +- [ ] 含負數 / 0 / 大數 +- [ ] 大資料量(接近上限) + +## 額外案例 +### 案例 1 +- 輸入: +- 預期: +- 說明: + +### 案例 2 +- 輸入: +- 預期: +- 說明: + diff --git a/problems/3227-vowels-game-in-a-string/test/main_test.go b/problems/3227-vowels-game-in-a-string/test/main_test.go new file mode 100644 index 0000000..8cf1402 --- /dev/null +++ b/problems/3227-vowels-game-in-a-string/test/main_test.go @@ -0,0 +1,188 @@ +// LeetCode 3227: Vowels Game in a String 單元測試(Go testing) +// Problem: Alice and Bob play a game. Alice removes substrings with odd vowels, +// Bob removes substrings with even vowels. Alice wins if string has any vowels. +package vowels_test + +import ( + "fmt" + "testing" + vowels "leetcode/3227-vowels-game-in-a-string/go" +) + +func TestExample1(t *testing.T) { + // "leetcode" contains vowels: e,e,o,e + input := "leetcode" + got := vowels.DoesAliceWin(input) + want := true // Alice wins + if got != want { + t.Fatalf("input %q: want %v got %v", input, want, got) + } +} + +func TestExample2(t *testing.T) { + // "bcdf" has no vowels + input := "bcdf" + got := vowels.DoesAliceWin(input) + want := false // Bob wins (Alice can't make first move) + if got != want { + t.Fatalf("input %q: want %v got %v", input, want, got) + } +} + +func TestAllVowels(t *testing.T) { + // All vowels string - Alice removes entire string + input := "aeiou" + got := vowels.DoesAliceWin(input) + want := true + if got != want { + t.Fatalf("input %q: want %v got %v", input, want, got) + } +} + +func TestSingleVowel(t *testing.T) { + // Single vowel - Alice removes it immediately + input := "a" + got := vowels.DoesAliceWin(input) + want := true + if got != want { + t.Fatalf("input %q: want %v got %v", input, want, got) + } +} + +func TestSingleConsonant(t *testing.T) { + // Single consonant - Alice can't move + input := "b" + got := vowels.DoesAliceWin(input) + want := false + if got != want { + t.Fatalf("input %q: want %v got %v", input, want, got) + } +} + +func TestMixedString(t *testing.T) { + // "programming" has vowels: o,a,i + input := "programming" + got := vowels.DoesAliceWin(input) + want := true // Alice can remove vowels + if got != want { + t.Fatalf("input %q: want %v got %v", input, want, got) + } +} + +func TestLongStringNoVowels(t *testing.T) { + // All consonants - no vowels means Bob wins + input := "bcdfghjklmnpqrstvwxyz" + got := vowels.DoesAliceWin(input) + want := false + if got != want { + t.Fatalf("input %q: want %v got %v", input, want, got) + } +} + +func TestRepeatedVowels(t *testing.T) { + // Even number of same vowels - Alice can leave 1 for Bob + input := "aaaa" + got := vowels.DoesAliceWin(input) + want := true + if got != want { + t.Fatalf("input %q: want %v got %v", input, want, got) + } +} + +func TestEdgeCases(t *testing.T) { + testCases := []struct { + input string + want bool + desc string + }{ + {"e", true, "single vowel"}, + {"x", false, "single consonant"}, + {"ab", true, "has vowel 'a'"}, + {"xy", false, "no vowels"}, + {"xabc", true, "vowel in middle"}, + {"abcx", true, "vowel at start"}, + {"xbca", true, "vowel at end"}, + } + + for _, tc := range testCases { + t.Run(tc.desc, func(t *testing.T) { + got := vowels.DoesAliceWin(tc.input) + if got != tc.want { + t.Fatalf("input %q (%s): want %v got %v", tc.input, tc.desc, tc.want, got) + } + }) + } +} + +func TestGameTheoryValidation(t *testing.T) { + testCases := []struct { + input string + want bool + desc string + }{ + // If total vowels = 0: Alice loses + {"bcdfg", false, "no vowels - Alice can't move"}, + {"rhythm", false, "no vowels (y not counted)"}, + + // Any vowels > 0: Alice wins + {"hello", true, "e,o vowels - Alice wins"}, + {"beautiful", true, "e,a,u,i,u vowels - Alice wins"}, + {"code", true, "o,e vowels - Alice wins"}, + {"education", true, "e,u,a,i,o vowels - Alice wins"}, + {"queue", true, "u,e,u,e vowels - Alice wins"}, + } + + for _, tc := range testCases { + t.Run(tc.desc, func(t *testing.T) { + got := vowels.DoesAliceWin(tc.input) + if got != tc.want { + t.Fatalf("input %q (%s): want %v got %v", tc.input, tc.desc, tc.want, got) + } + }) + } +} + +func TestParameterized(t *testing.T) { + testCases := []struct { + input string + want bool + }{ + {"", false}, // empty string + {"aeiou", true}, // all vowels + {"bcdfg", false}, // all consonants + {"hello", true}, // mixed with vowels + {"rhythm", false}, // no vowels + {"queue", true}, // multiple same vowels + {"xyz", false}, // short no vowels + {"programming", true}, // longer mixed + } + + for _, tc := range testCases { + t.Run(tc.input, func(t *testing.T) { + got := vowels.DoesAliceWin(tc.input) + if got != tc.want { + t.Errorf("doesAliceWin(%q) = %v, want %v", tc.input, got, tc.want) + } + }) + } +} + +// Benchmark test +func BenchmarkDoesAliceWin(b *testing.B) { + testString := "abcdefghijklmnopqrstuvwxyz" + b.ResetTimer() + for i := 0; i < b.N; i++ { + vowels.DoesAliceWin(testString) + } +} + +// Example test for documentation +func ExampleDoesAliceWin() { + result1 := vowels.DoesAliceWin("leetcode") + result2 := vowels.DoesAliceWin("bcdf") + fmt.Println(result1) + fmt.Println(result2) + // Output: + // true + // false +}