2490. Circular Sentence
Problem Description
You are given a sentence that consists of words separated by single spaces, with no leading or trailing spaces. Each word contains only uppercase and lowercase English letters, where uppercase and lowercase letters are treated as different characters.
A sentence is considered circular if it meets two conditions:
- The last character of each word matches the first character of the next word
- The last character of the final word matches the first character of the first word
For example:
-
"leetcode exercises sound delightful"
is circular because:- 'leetcode' ends with 'e', 'exercises' starts with 'e'
- 'exercises' ends with 's', 'sound' starts with 's'
- 'sound' ends with 'd', 'delightful' starts with 'd'
- 'delightful' ends with 'l', 'leetcode' starts with 'l'
-
"Leetcode is cool"
is not circular because 'Leetcode' ends with 'e' but 'is' starts with 'i'
The solution splits the sentence into words and checks if each word's last character equals the next word's first character. It uses modulo arithmetic (i + 1) % n
to wrap around and compare the last word with the first word, ensuring the circular property is satisfied.
Given a string sentence
, return true
if it forms a circular sentence, otherwise return false
.
Intuition
The key insight is recognizing that we need to check a chain of connections between consecutive words, where each connection is formed by matching the last character of one word with the first character of the next word.
Think of the words as forming a circle where each word must "link" to the next one through matching characters. Like a chain where each link must properly connect to form a complete loop.
We can break this down into checking pairs of adjacent words:
- Word 1's last character must match Word 2's first character
- Word 2's last character must match Word 3's first character
- And so on...
The tricky part is remembering that it's circular - the last word must also connect back to the first word. This means after checking all consecutive pairs, we need one final check: does the last word's ending character match the first word's starting character?
Instead of writing separate logic for the wraparound case, we can use modulo arithmetic. When we're at word i
, we check if it connects to word (i + 1) % n
. This elegantly handles the wraparound: when i
is the last index n-1
, then (n-1 + 1) % n = 0
, which correctly points us back to the first word.
The solution becomes clean: split the sentence into words, then verify that every word i
has its last character s[-1]
matching the first character ss[(i + 1) % n][0]
of the next word in the circular sequence. If all pairs satisfy this condition, the sentence is circular.
Solution Approach
The implementation follows a straightforward simulation approach where we check each word connection in the sentence.
Step 1: Split the sentence into words
ss = sentence.split()
We use Python's split()
method to break the sentence into individual words. This automatically handles multiple spaces and gives us a list of words.
Step 2: Get the number of words
n = len(ss)
Store the total count of words for use in the modulo operation.
Step 3: Check all word connections
all(s[-1] == ss[(i + 1) % n][0] for i, s in enumerate(ss))
This line does all the heavy lifting using Python's all()
function with a generator expression:
enumerate(ss)
gives us each words
along with its indexi
- For each word at index
i
, we access:s[-1]
: the last character of the current wordss[(i + 1) % n][0]
: the first character of the next word
- The modulo operation
(i + 1) % n
ensures that when we reach the last word (indexn-1
), the next index wraps around to0
, checking the connection back to the first word
The all()
function returns True
only if every comparison in the generator expression evaluates to True
. If even one word doesn't connect properly to its next word, the function returns False
.
Time Complexity: O(n)
where n
is the number of words, as we iterate through each word once.
Space Complexity: O(n)
for storing the split words in the list ss
.
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through the solution with the example: "apple eagle eat ta"
Step 1: Split the sentence into words
ss = ["apple", "eagle", "eat", "ta"]
Step 2: Get the number of words
n = 4
Step 3: Check each word connection
Now let's trace through the generator expression for each iteration:
Iteration 0: i = 0, s = "apple"
- Current word's last char:
s[-1] = 'e'
- Next word index:
(0 + 1) % 4 = 1
- Next word's first char:
ss[1][0] = 'e'
(from "eagle") - Check:
'e' == 'e'
✓ True
Iteration 1: i = 1, s = "eagle"
- Current word's last char:
s[-1] = 'e'
- Next word index:
(1 + 1) % 4 = 2
- Next word's first char:
ss[2][0] = 'e'
(from "eat") - Check:
'e' == 'e'
✓ True
Iteration 2: i = 2, s = "eat"
- Current word's last char:
s[-1] = 't'
- Next word index:
(2 + 1) % 4 = 3
- Next word's first char:
ss[3][0] = 't'
(from "ta") - Check:
't' == 't'
✓ True
Iteration 3: i = 3, s = "ta"
- Current word's last char:
s[-1] = 'a'
- Next word index:
(3 + 1) % 4 = 0
(wraps around!) - Next word's first char:
ss[0][0] = 'a'
(from "apple") - Check:
'a' == 'a'
✓ True
Since all four checks returned True
, the all()
function returns True
, confirming this is a circular sentence.
The key insight is how the modulo operation (3 + 1) % 4 = 0
naturally handles the wraparound, allowing us to check if the last word "ta" connects back to the first word "apple" without any special case logic.
Solution Implementation
1class Solution:
2 def isCircularSentence(self, sentence: str) -> bool:
3 # Split the sentence into individual words
4 words = sentence.split()
5
6 # Get the number of words in the sentence
7 num_words = len(words)
8
9 # Check if each word's last character matches the next word's first character
10 # Use modulo operator to wrap around from last word to first word
11 for i, word in enumerate(words):
12 next_word_index = (i + 1) % num_words
13
14 # Check if current word's last character equals next word's first character
15 if word[-1] != words[next_word_index][0]:
16 return False
17
18 return True
19
1class Solution {
2 /**
3 * Checks if a sentence is circular.
4 * A sentence is circular if the last character of each word equals
5 * the first character of the next word, and the last character of
6 * the last word equals the first character of the first word.
7 *
8 * @param sentence the input sentence containing words separated by spaces
9 * @return true if the sentence is circular, false otherwise
10 */
11 public boolean isCircularSentence(String sentence) {
12 // Split the sentence into individual words
13 String[] words = sentence.split(" ");
14 int wordCount = words.length;
15
16 // Check each word's last character against the next word's first character
17 for (int i = 0; i < wordCount; i++) {
18 // Get the current word and the next word (wrapping around using modulo)
19 String currentWord = words[i];
20 String nextWord = words[(i + 1) % wordCount];
21
22 // Get the last character of current word
23 char lastCharOfCurrent = currentWord.charAt(currentWord.length() - 1);
24 // Get the first character of next word
25 char firstCharOfNext = nextWord.charAt(0);
26
27 // If characters don't match, sentence is not circular
28 if (lastCharOfCurrent != firstCharOfNext) {
29 return false;
30 }
31 }
32
33 // All adjacent words satisfy the circular condition
34 return true;
35 }
36}
37
1class Solution {
2public:
3 /**
4 * Checks if a sentence is circular.
5 * A sentence is circular if the last character of each word equals
6 * the first character of the next word (including wrap-around from last to first word).
7 *
8 * @param sentence The input sentence containing words separated by spaces
9 * @return true if the sentence is circular, false otherwise
10 */
11 bool isCircularSentence(string sentence) {
12 // Split the sentence into individual words
13 vector<string> words = split(sentence, ' ');
14 int wordCount = words.size();
15
16 // Check circular condition for each consecutive pair of words
17 for (int i = 0; i < wordCount; ++i) {
18 // Get the last character of current word
19 char lastCharOfCurrentWord = words[i].back();
20 // Get the first character of next word (with wrap-around using modulo)
21 char firstCharOfNextWord = words[(i + 1) % wordCount][0];
22
23 // If characters don't match, sentence is not circular
24 if (lastCharOfCurrentWord != firstCharOfNextWord) {
25 return false;
26 }
27 }
28
29 // All consecutive pairs satisfy the circular condition
30 return true;
31 }
32
33private:
34 /**
35 * Splits a string into tokens based on a delimiter character.
36 *
37 * @param str The string to be split
38 * @param delimiter The character used to separate tokens
39 * @return A vector containing all the tokens
40 */
41 vector<string> split(string& str, char delimiter) {
42 stringstream stringStream(str);
43 string token;
44 vector<string> tokens;
45
46 // Extract tokens separated by the delimiter
47 while (getline(stringStream, token, delimiter)) {
48 tokens.emplace_back(token);
49 }
50
51 return tokens;
52 }
53};
54
1/**
2 * Checks if a sentence is circular - where the last character of each word
3 * matches the first character of the next word, and the last character of
4 * the last word matches the first character of the first word.
5 *
6 * @param sentence - The input sentence to check
7 * @returns true if the sentence is circular, false otherwise
8 */
9function isCircularSentence(sentence: string): boolean {
10 // Split the sentence into an array of words
11 const words: string[] = sentence.split(' ');
12
13 // Get the total number of words
14 const wordCount: number = words.length;
15
16 // Check each word's last character against the next word's first character
17 for (let i = 0; i < wordCount; i++) {
18 // Get the current word and the next word (wrapping around to first word)
19 const currentWord: string = words[i];
20 const nextWord: string = words[(i + 1) % wordCount];
21
22 // Get the last character of current word
23 const lastCharOfCurrent: string = currentWord[currentWord.length - 1];
24
25 // Get the first character of next word
26 const firstCharOfNext: string = nextWord[0];
27
28 // If characters don't match, sentence is not circular
29 if (lastCharOfCurrent !== firstCharOfNext) {
30 return false;
31 }
32 }
33
34 // All adjacent words satisfy the circular condition
35 return true;
36}
37
Time and Space Complexity
The time complexity is O(n)
, where n
is the length of the input string sentence
.
- The
split()
operation traverses the entire string once to split it into words:O(n)
- The
enumerate()
creates an iterator over the list of words:O(1)
to create,O(m)
to iterate wherem
is the number of words - For each word, we access the last character
s[-1]
and the first character of the next wordss[(i + 1) % n][0]
:O(1)
per word - The
all()
function evaluates the generator expression for all words:O(m)
wherem
is the number of words - Since the total length of all words combined is at most
n
, the overall time complexity isO(n)
The space complexity is O(n)
, where n
is the length of the input string sentence
.
- The
split()
operation creates a new listss
containing all words from the sentence, which requiresO(n)
space to store all characters from the original string (distributed across multiple word strings) - The generator expression in
all()
usesO(1)
additional space as it evaluates one condition at a time - Therefore, the total space complexity is
O(n)
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Forgetting to Handle Single-Word Sentences
A common mistake is assuming there will always be multiple words. For a single-word sentence like "a"
or "aba"
, the circular condition requires the word's last character to match its own first character.
Pitfall Example:
# Incorrect approach that might fail for single words
def isCircularSentence(self, sentence: str) -> bool:
words = sentence.split()
if len(words) == 1:
return True # Wrong! Should check if first and last char match
# ... rest of the code
Solution:
The modulo approach naturally handles this case. When there's only one word, (0 + 1) % 1 = 0
, so it correctly compares the word's last character with its own first character.
2. Using Space-Based Character Checking Instead of Word Splitting
Some might try to check characters directly around spaces without properly splitting into words first.
Pitfall Example:
# Incorrect approach
def isCircularSentence(self, sentence: str) -> bool:
# This fails because it doesn't handle the wrap-around case
for i in range(len(sentence)):
if sentence[i] == ' ':
if sentence[i-1] != sentence[i+1]:
return False
return True
Problems with this approach:
- Doesn't check if the last word connects to the first word
- Index out of bounds errors at sentence boundaries
- More complex logic needed to find word boundaries
Solution:
Always split the sentence into words first using split()
, then work with the word list. This makes the logic cleaner and the modulo wrap-around straightforward.
3. Case Sensitivity Confusion
The problem states that uppercase and lowercase letters are different characters. A common mistake is normalizing the case.
Pitfall Example:
# Incorrect - normalizing case when we shouldn't
def isCircularSentence(self, sentence: str) -> bool:
words = sentence.lower().split() # Wrong! 'A' and 'a' are different
# ... rest of the code
Solution:
Keep the original case of the sentence. The characters 'A' and 'a' should be treated as different, so "Alpha apple"
would NOT be circular since 'a' ≠ 'a'.
You are given an array of intervals where intervals[i] = [start_i, end_i]
represent the start and end of the ith
interval. You need to merge all overlapping intervals and return an array of the non-overlapping intervals that cover all the intervals in the input.
Recommended Readings
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Runtime Overview When learning about algorithms and data structures you'll frequently encounter the term time complexity This concept is fundamental in computer science and offers insights into how long an algorithm takes to complete given a certain input size What is Time Complexity Time complexity represents the amount of time
Want a Structured Path to Master System Design Too? Don’t Miss This!