Capitalize First and Last Letter of Each Word: Python, C, Java
Capitalize the first and last letter of each word in a string. Covers word-boundary detection, edge cases, and complete code in Python, C, and Java.
Capitalising the first and last letter of every word in a string is a single-pass O(n) problem that tests whether you can detect word boundaries cleanly.
The core insight: a character is the first letter of a word if it sits at index 0 or immediately after a space. It is the last letter of a word if the next character is a space or it sits at the final index. Apply toupper at both positions, skip spaces, and you’re done.
The Problem Statement
Given a string containing words separated by one or more spaces, convert the first and last character of each word to uppercase. Leave all other characters unchanged.
Worked example (traced character-by-character):
- Input:
"hello world" - Word 1 (“hello”): index 0 is first letter (h to H), index 4 is last letter (o to O)
- Word 2 (“world”): index 6 is first letter after space (w to W), index 10 is last letter at end-of-string (d to D)
- Output:
"HellO WorlD"
Three edge cases define whether your solution is complete:
- Single-character word (“a”): only one letter exists, capitalised once, result is “A”
- Two-character word (“me”): first letter and last letter are different indices (0 and 1), both capitalised, result is “ME”
- Multiple consecutive spaces (“hi there”): spaces are not letters, the guard skips them, result is “HI TherE”
Word-Boundary Detection Logic
The algorithm reduces to two boolean checks per character at index i:
is_first = (i == 0) or (str[i-1] == ' ' and str[i] != ' ')
is_last = (i == n-1) or (str[i+1] == ' ' and str[i] != ' ')
If either condition is true, convert the character to uppercase. The str[i] != ' ' guard prevents capitalising a space character when multiple spaces appear between words.
The case conversion itself uses the ASCII arithmetic trick: for a lowercase letter in the range 'a' to 'z', subtracting 32 gives the uppercase equivalent. Library functions like Python’s str.upper() and C’s toupper() handle this internally and also return non-alphabetic characters unchanged. This means you don’t need a separate isalpha check before calling toupper. A digit or punctuation character passes through without modification.
Related Patterns
This boundary-detection approach is the same skeleton used in title-case conversion, toggle-case problems, and sentence capitalisation. The only difference is which indices trigger the conversion. Once you internalise the “check the neighbour” pattern, all of these become single-pass traversals with minor condition changes.
Code: Python
def capitalize_first_last(s):
words = s.split(' ')
result = []
for word in words:
if len(word) == 0:
result.append(word)
elif len(word) == 1:
result.append(word.upper())
else:
result.append(word[0].upper() + word[1:-1] + word[-1].upper())
return ' '.join(result)
# Test
print(capitalize_first_last("hello world")) # HellO WorlD
print(capitalize_first_last("a bc")) # A BC
print(capitalize_first_last("hi there")) # HI TherE
The split(' ') call (with an explicit space argument) preserves empty strings for consecutive spaces. This is different from split() with no argument, which collapses all whitespace runs into a single delimiter and strips leading and trailing spaces. For this problem, you need split(' ') to keep the original spacing intact in the output.
The Python version uses slicing: word[0] gives the first character, word[-1] gives the last, and word[1:-1] gives everything in between. For a two-character word like “me”, word[1:-1] is an empty string, so the result is just word[0].upper() + word[-1].upper().
Code: C
#include <stdio.h>
#include <ctype.h>
#include <string.h>
void capitalize_first_last(char str[]) {
int n = strlen(str);
for (int i = 0; i < n; i++) {
if (str[i] == ' ')
continue;
/* First letter of a word */
if (i == 0 || str[i - 1] == ' ')
str[i] = toupper((unsigned char)str[i]);
/* Last letter of a word */
if (i == n - 1 || str[i + 1] == ' ')
str[i] = toupper((unsigned char)str[i]);
}
}
int main() {
char s1[] = "hello world";
capitalize_first_last(s1);
printf("%s\n", s1); /* HellO WorlD */
char s2[] = "a bc";
capitalize_first_last(s2);
printf("%s\n", s2); /* A BC */
char s3[] = "hi there";
capitalize_first_last(s3);
printf("%s\n", s3); /* HI TherE */
return 0;
}
The C version modifies the character array in place. No extra memory is allocated beyond the original string. The (unsigned char) cast before toupper prevents undefined behaviour when the input contains characters with values above 127.
Note the continue statement at the top of the loop. It skips space characters early, so the first-letter and last-letter checks only run on actual word characters. This keeps the logic cleaner than nesting everything inside an if (str[i] != ' ') block.
Code: Java
public class CapitalizeFirstLast {
public static String capitalizeFirstLast(String s) {
char[] arr = s.toCharArray();
int n = arr.length;
for (int i = 0; i < n; i++) {
if (arr[i] == ' ')
continue;
// First letter of a word
if (i == 0 || arr[i - 1] == ' ')
arr[i] = Character.toUpperCase(arr[i]);
// Last letter of a word
if (i == n - 1 || arr[i + 1] == ' ')
arr[i] = Character.toUpperCase(arr[i]);
}
return new String(arr);
}
public static void main(String[] args) {
System.out.println(capitalizeFirstLast("hello world")); // HellO WorlD
System.out.println(capitalizeFirstLast("a bc")); // A BC
System.out.println(capitalizeFirstLast("hi there")); // HI TherE
}
}
Java strings are immutable. The toCharArray() call creates a mutable copy; after processing, new String(arr) produces the final result. The logic mirrors the C version exactly, using Character.toUpperCase() instead of the C library’s toupper.
Edge Cases and Trace
Verifying the three critical edge cases against the Java/C logic:
-
Single-character word “a” (index 0):
i = 0: not a space, passes thecontinueguard- First-letter check:
i == 0is true, capitalise to ‘A’ - Last-letter check:
i == n - 1(0 == 0) is true, capitalise again (already ‘A’) - Result: “A”
-
Two-character word “me” (indices 0, 1):
i = 0: first-letter check true (capitalise ‘m’ to ‘M’), last-letter check false (arr[1]is ‘e’, not space)i = 1: first-letter check false (arr[0]is ‘M’, not space), last-letter check true (i == n - 1), capitalise ‘e’ to ‘E’- Result: “ME”
-
Multiple spaces “hi there” (length 9):
i = 0: ‘h’, first-letter true, capitalise to ‘H’i = 1: ‘i’, last-letter true (arr[2]is ’ ’), capitalise to ‘I’i = 2, 3: spaces,continuei = 4: ‘t’, first-letter true (arr[3]is ’ ’), capitalise to ‘T’i = 8: ‘e’, last-letter true (i == n - 1), capitalise to ‘E’- Result: “HI TherE”
All three traces match expected output. No overlap bugs, no off-by-one errors.
In-Place vs Copy Tradeoff
The C implementation uses O(1) extra space by modifying the input array. Python and Java necessarily allocate new memory because their string types are immutable.
For placement coding rounds, the in-place approach is usually preferred when the language allows it. It demonstrates awareness of memory constraints, which matters when the same pattern appears in problems operating on larger inputs. The space-complexity article covers how to reason about auxiliary space in more depth.
The character-level pattern here (iterate once, modify conditionally at each index) reappears in related problems like replacing digits in an integer and computing digit sums. Building fluency with one makes the others faster to solve under time pressure.
Where This Appears in Placements
String manipulation questions of this type show up in online assessment rounds at service-tier companies (TCS, Infosys, Wipro) and in on-campus coding tests administered through platforms like CoCubes and AMCAT Automata. The underlying skill being tested is not the capitalisation itself but the clean detection of word boundaries in a single pass without extra data structures.
The same boundary-detection logic extends to NLP pre-processing in AI systems. Tokenizers split text at word boundaries before feeding tokens to a model, and the “check the neighbour character” pattern you just wrote is the same primitive those tokenizers use at the character level. If you want to see how that ASCII-level text processing connects to real model input, TinkerLLM lets you type a sentence and watch a tokenizer split it, apply case normalisation, and produce embeddings live in your browser. It costs ₹299 and makes the connection between this kind of single-pass string traversal and production AI pipelines concrete.
Primary sources
Frequently asked questions
What is the output for the input hello world?
The output is HellO WorlD. The first and last letters of each word are capitalised: h becomes H, o becomes O in hello; w becomes W, d becomes D in world. Middle letters stay lowercase.
How do you handle a single-character word like a?
A single-character word is both the first and last letter of itself. The algorithm capitalises it once. No special-case branch is needed if the first-letter and last-letter conditions both apply to the same index without conflict.
What happens when there are multiple spaces between words?
The non-space guard prevents the algorithm from calling toupper on space characters. Consecutive spaces are left unchanged. The next non-space character after a run of spaces is treated as the first letter of the next word.
What is the time complexity?
O(n) where n is the length of the input string. Each character is visited exactly once in a single pass. No nested loops or repeated scans are involved.
Does this work with punctuation at the end of a word?
If a word ends with punctuation like a comma or period, the algorithm capitalises that punctuation character. Since toupper on a non-alphabetic character returns it unchanged, the output is correct. No extra handling is needed.
Why does the C version modify the string in place while Python and Java do not?
C strings are mutable character arrays, so direct modification costs no extra memory. Python strings and Java Strings are immutable objects. Modifying them requires building a new string or converting to a mutable structure like a char array first.
A self-paced playground for building with LLMs.
TinkerLLM is FACE Prep's sister property. A guided environment for shipping real LLM applications, the kind of project that earns a paragraph on your resume, not a line.
Try TinkerLLM (₹299 launch)