├── Cargo.toml ├── .github └── workflows │ ├── cd.yml │ └── ci.yml ├── .gitignore ├── tests └── integration_test.rs ├── LICENSE ├── src └── lib.rs └── README.md /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "word-ladder" 3 | version = "0.1.0" 4 | edition = "2021" 5 | authors = ["aliezzahn "] 6 | description = "A Rust implementation of the Word Ladder problem." 7 | license = "MIT" 8 | repository = "https://github.com/aliezzahn/word-ladder" 9 | 10 | [dependencies] 11 | 12 | [dev-dependencies] -------------------------------------------------------------------------------- /.github/workflows/cd.yml: -------------------------------------------------------------------------------- 1 | name: CD 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v3 13 | - name: Set up Rust 14 | uses: actions-rs/toolchain@v1 15 | with: 16 | toolchain: stable 17 | override: true 18 | - name: Build project 19 | run: cargo build --release 20 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | branches: 9 | - main 10 | 11 | jobs: 12 | test: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v3 16 | - name: Set up Rust 17 | uses: actions-rs/toolchain@v1 18 | with: 19 | toolchain: stable 20 | override: true 21 | - name: Run tests 22 | run: cargo test 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | debug/ 4 | target/ 5 | 6 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 7 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 8 | Cargo.lock 9 | 10 | # These are backup files generated by rustfmt 11 | **/*.rs.bk 12 | 13 | # MSVC Windows builds of rustc generate these, which store debugging information 14 | *.pdb 15 | 16 | # RustRover 17 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 18 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 19 | # and can be added to the global gitignore or merged into this file. For a more nuclear 20 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 21 | #.idea/ 22 | 23 | # Added by cargo 24 | 25 | /target 26 | -------------------------------------------------------------------------------- /tests/integration_test.rs: -------------------------------------------------------------------------------- 1 | use word_ladder::ladder_length; 2 | 3 | #[test] 4 | fn test_ladder_length() { 5 | let begin_word = String::from("hit"); 6 | let end_word = String::from("cog"); 7 | let word_list = vec![ 8 | String::from("hot"), 9 | String::from("dot"), 10 | String::from("dog"), 11 | String::from("lot"), 12 | String::from("log"), 13 | String::from("cog"), 14 | ]; 15 | assert_eq!(ladder_length(begin_word, end_word, word_list), 5); 16 | } 17 | 18 | #[test] 19 | fn test_no_possible_transformation() { 20 | let begin_word = String::from("hit"); 21 | let end_word = String::from("cog"); 22 | let word_list = vec![ 23 | String::from("hot"), 24 | String::from("dot"), 25 | String::from("dog"), 26 | String::from("lot"), 27 | String::from("log"), 28 | ]; 29 | assert_eq!(ladder_length(begin_word, end_word, word_list), 0); 30 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 aliezza hn 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::collections::{HashSet, VecDeque}; 2 | 3 | pub fn ladder_length(begin_word: String, end_word: String, word_list: Vec) -> i32 { 4 | let word_set: HashSet = word_list.into_iter().collect(); 5 | 6 | // If the end_word is not in the word_list, return 0 7 | if !word_set.contains(&end_word) { 8 | return 0; 9 | } 10 | 11 | let mut queue = VecDeque::new(); 12 | queue.push_back((begin_word.clone(), 1)); 13 | 14 | // Remove the begin_word from the set to avoid revisiting 15 | let mut word_set = word_set; 16 | word_set.remove(&begin_word); 17 | 18 | while let Some((current_word, steps)) = queue.pop_front() { 19 | // If we reach the end_word, return the number of steps 20 | if current_word == end_word { 21 | return steps; 22 | } 23 | 24 | // Generate all possible one-letter transformations 25 | for i in 0..current_word.len() { 26 | let mut chars: Vec = current_word.chars().collect(); 27 | for c in 'a'..='z' { 28 | chars[i] = c; 29 | let new_word: String = chars.iter().collect(); 30 | 31 | // If the new_word is in the set, add it to the queue 32 | if word_set.contains(&new_word) { 33 | queue.push_back((new_word.clone(), steps + 1)); 34 | word_set.remove(&new_word); // Avoid revisiting 35 | } 36 | } 37 | } 38 | } 39 | 40 | // If we exhaust the queue without finding the end_word, return 0 41 | 0 42 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Word Ladder 2 | 3 | ![CI](https://github.com/aliezzahn/word-ladder/actions/workflows/ci.yml/badge.svg) 4 | ![CD](https://github.com/aliezzahn/word-ladder/actions/workflows/cd.yml/badge.svg) 5 | 6 | A Rust implementation of the Word Ladder problem. 7 | 8 | ## Problem Description 9 | 10 | Given two words (`begin_word` and `end_word`), and a dictionary of words (`word_list`), find the length of the shortest transformation sequence from `begin_word` to `end_word` such that: 11 | 12 | - Only one letter can be changed at a time. 13 | - Each transformed word must exist in the `word_list`. 14 | 15 | ## Usage 16 | 17 | ### Installation 18 | 19 | Add this crate to your `Cargo.toml`: 20 | 21 | ```toml 22 | [dependencies] 23 | word-ladder = "0.1.0" 24 | ``` 25 | 26 | ### Example 27 | 28 | ```rust 29 | use word_ladder::ladder_length; 30 | 31 | fn main() { 32 | let begin_word = String::from("hit"); 33 | let end_word = String::from("cog"); 34 | let word_list = vec![ 35 | String::from("hot"), 36 | String::from("dot"), 37 | String::from("dog"), 38 | String::from("lot"), 39 | String::from("log"), 40 | String::from("cog"), 41 | ]; 42 | let result = ladder_length(begin_word, end_word, word_list); 43 | println!("Transformation steps: {}", result); // Output: 5 44 | } 45 | ``` 46 | 47 | ## Running Tests 48 | 49 | To run the tests, use the following command: 50 | 51 | ```bash 52 | cargo test 53 | ``` 54 | 55 | ## Contributing 56 | 57 | Contributions are welcome! Please open an issue or submit a pull request. 58 | 59 | ## License 60 | 61 | This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details. 62 | --------------------------------------------------------------------------------