Create a basic sentence checker that takes in a stream of characters and determines whether they form valid sentences. If a sentence is valid, the program should print it out.
We can consider a sentence valid if it conforms to the following rules:
The sentence must start with a capital letter, followed by a lowercase letter or a space.
All other characters must be lowercase letters, separators (,,;,:) or terminal marks (.,?,!,‽).
There must be a single space between each word.
The sentence must end with a terminal mark immediately following a word.
Input: "Hello world!"
Output: "Hello world!"
Explanation: Valid sentence - starts with capital, has single space between words, ends with terminal mark.
We can model this problem as a finite state machine where we track the current state of sentence parsing. The key insight is that we need to validate characters sequentially while maintaining context about what characters are expected next based on the current position in the sentence.
classSolution {
public:bool isValidSentence(string s) {
if (s.empty()) return false;
enumState { START, FIRST_CHAR, WORD, SPACE, SEPARATOR, TERMINAL };
State state = START;
for (int i =0; i < s.length(); i++) {
char c = s[i];
switch (state) {
case START:
if (isupper(c)) {
state = FIRST_CHAR;
} else {
return false;
}
break;
case FIRST_CHAR:
if (islower(c)) {
state = WORD;
} elseif (c ==' ') {
return false; // Space after single capital letter
} elseif (isTerminal(c)) {
state = TERMINAL;
} else {
return false;
}
break;
case WORD:
if (islower(c)) {
// Stay in WORD
} elseif (c ==' ') {
state = SPACE;
} elseif (isSeparator(c)) {
state = SEPARATOR;
} elseif (isTerminal(c)) {
state = TERMINAL;
} else {
return false;
}
break;
case SPACE:
if (islower(c)) {
state = WORD;
} else {
return false; // No double spaces or other chars after space
}
break;
case SEPARATOR:
if (c ==' ') {
state = SPACE;
} elseif (islower(c)) {
state = WORD;
} elseif (isTerminal(c)) {
state = TERMINAL;
} else {
return false;
}
break;
case TERMINAL:
return false; // Nothing should follow terminal
}
}
return state == TERMINAL;
}
private:bool isSeparator(char c) {
return c ==','|| c ==';'|| c ==':';
}
boolisTerminal(char c) {
return c =='.'|| c =='?'|| c =='!'|| c =='‽';
}
};
classSolution {
privateenum State {
START, FIRST_CHAR, WORD, SPACE, SEPARATOR, TERMINAL
}
publicbooleanisValidSentence(String s) {
if (s.isEmpty()) returnfalse;
State state = State.START;
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
switch (state) {
case START:
if (Character.isUpperCase(c)) {
state = State.FIRST_CHAR;
} else {
returnfalse;
}
break;
case FIRST_CHAR:
if (Character.isLowerCase(c)) {
state = State.WORD;
} elseif (c ==' ') {
returnfalse;
} elseif (isTerminal(c)) {
state = State.TERMINAL;
} else {
returnfalse;
}
break;
case WORD:
if (Character.isLowerCase(c)) {
// Stay in WORD } elseif (c ==' ') {
state = State.SPACE;
} elseif (isSeparator(c)) {
state = State.SEPARATOR;
} elseif (isTerminal(c)) {
state = State.TERMINAL;
} else {
returnfalse;
}
break;
case SPACE:
if (Character.isLowerCase(c)) {
state = State.WORD;
} else {
returnfalse;
}
break;
case SEPARATOR:
if (c ==' ') {
state = State.SPACE;
} elseif (Character.isLowerCase(c)) {
state = State.WORD;
} elseif (isTerminal(c)) {
state = State.TERMINAL;
} else {
returnfalse;
}
break;
case TERMINAL:
returnfalse;
}
}
return state == State.TERMINAL;
}
privatebooleanisSeparator(char c) {
return c ==','|| c ==';'|| c ==':';
}
privatebooleanisTerminal(char c) {
return c =='.'|| c =='?'|| c =='!'|| c =='‽';
}
}
classSolution:
defis_valid_sentence(self, s: str) -> bool:
ifnot s:
returnFalse START, FIRST_CHAR, WORD, SPACE, SEPARATOR, TERMINAL =0, 1, 2, 3, 4, 5 state = START
for c in s:
if state == START:
if c.isupper():
state = FIRST_CHAR
else:
returnFalseelif state == FIRST_CHAR:
if c.islower():
state = WORD
elif c ==' ':
returnFalseelif self._is_terminal(c):
state = TERMINAL
else:
returnFalseelif state == WORD:
if c.islower():
pass# Stay in WORDelif c ==' ':
state = SPACE
elif self._is_separator(c):
state = SEPARATOR
elif self._is_terminal(c):
state = TERMINAL
else:
returnFalseelif state == SPACE:
if c.islower():
state = WORD
else:
returnFalseelif state == SEPARATOR:
if c ==' ':
state = SPACE
elif c.islower():
state = WORD
elif self._is_terminal(c):
state = TERMINAL
else:
returnFalseelif state == TERMINAL:
returnFalsereturn state == TERMINAL
def_is_separator(self, c: str) -> bool:
return c in',;:'def_is_terminal(self, c: str) -> bool:
return c in'.?!‽'
We can use regular expressions to define the exact pattern that a valid sentence must match. This approach is more concise but potentially less efficient for streaming data.
classSolution {
public:bool isValidSentence(string s) {
if (s.empty()) return false;
// Pattern explanation:
// ^[A-Z] - starts with capital letter
// ([a-z]+([,;:]?( [a-z]+)*))? - optional word with separators and spaces
// [.?!‽]$ - ends with terminal mark
regex pattern("^[A-Z]([a-z]+([,;:]?( [a-z]+)*)?)*[.?!‽]$");
returnregex_match(s, pattern);
}
};
For streaming applications, we need to process characters one by one and maintain state between calls. This approach is most suitable when characters arrive incrementally rather than as a complete string.