Exercises: Strings — Text Processing and Manipulation

These exercises progress from foundational drills through applied problems to challenging projects. All code should be Python 3.12+.

Difficulty Guide: - ⭐ Foundational (5-10 min each) - ⭐⭐ Intermediate (10-20 min each) - ⭐⭐⭐ Challenging (20-40 min each) - ⭐⭐⭐⭐ Advanced/Research (40+ min each)


Part A: Indexing, Slicing, and Immutability ⭐

A.1. Given message = "Computational Thinking", what is the value of each expression? Predict first, then verify in Python: - a) message[0] - b) message[13] - c) message[-1] - d) message[-8] - e) len(message)

A.2. Given dna = "ATCGATCGATCG", write slice expressions that produce: - a) "ATCG" (first four characters) - b) "ATCG" (last four characters) - c) "GATC" (characters at indices 3-6) - d) "GCTAGCTA" (the string reversed) - e) "ACAC" (every third character)

A.3. Explain why the following code produces an error. Then fix it two different ways.

language = "Javon"
language[0] = "P"
language[3] = "h"
print(language)  # Intended: "Python"

A.4. Without running the code, predict what each line prints. Then verify.

s = "Hello, World!"
print(s[7:12])
print(s[::-1])
print(s[0:50])     # What happens with out-of-range slice?
print(s[::2])

A.5. Write a function middle_char(s) that returns the middle character of a string. If the string has an even number of characters, return the two middle characters.

print(middle_char("abc"))      # Expected: "b"
print(middle_char("abcd"))     # Expected: "bc"
print(middle_char("Python"))   # Expected: "th"

Part B: String Methods ⭐⭐

B.1. Write a function normalize_name(name) that takes a name in any format and returns it in "Last, First" format with proper capitalization.

print(normalize_name("  alice   CHEN  "))   # Expected: "Chen, Alice"
print(normalize_name("BOB martinez"))        # Expected: "Martinez, Bob"

Hint: Use strip(), split(), title() or capitalize().

B.2. Write a function count_vowels(text) that returns the number of vowels (a, e, i, o, u) in the given text, case-insensitive.

print(count_vowels("Hello, World!"))         # Expected: 3
print(count_vowels("AEIOU aeiou"))           # Expected: 10
print(count_vowels("rhythm"))                # Expected: 0

B.3. Write a function remove_punctuation(text) that removes all punctuation from a string. Punctuation characters to remove: .,!?;:'"()-

print(remove_punctuation("Hello, World!"))         # Expected: "Hello World"
print(remove_punctuation("It's a test (really).")) # Expected: "Its a test really"

Hint: Loop through each character and keep only non-punctuation ones, or chain replace() calls.

B.4. Given the following CSV data, write code that splits it into rows and then into fields, printing each person's name and department:

csv_data = """Name,Department,Years
Anika Patel,Biology,12
Carlos Rivera,Chemistry,7
Min-Jun Park,Computer Science,3
Sarah O'Brien,Mathematics,9"""

Expected output:

Anika Patel works in Biology
Carlos Rivera works in Chemistry
Min-Jun Park works in Computer Science
Sarah O'Brien works in Mathematics

B.5. Write a function initials(full_name) that returns a person's initials in uppercase.

print(initials("Anika Patel"))             # Expected: "A.P."
print(initials("mary jane watson"))         # Expected: "M.J.W."
print(initials("  cher  "))                # Expected: "C."

B.6. Write a function censor_word(text, word) that replaces every occurrence of word in text with asterisks of the same length. The censoring should be case-insensitive, but the rest of the text should keep its original case.

print(censor_word("The password is secret", "secret"))
# Expected: "The password is ******"
print(censor_word("Java java JAVA", "java"))
# Expected: "**** **** ****"

Hint: Use lower() for finding, but you'll need to think carefully about replacement.


Part C: Text Processing ⭐⭐

C.1. Write a function pig_latin(word) that converts a word to Pig Latin: - If the word starts with a vowel, add "yay" to the end - If the word starts with a consonant, move the first letter to the end and add "ay"

print(pig_latin("apple"))    # Expected: "appleyay"
print(pig_latin("hello"))    # Expected: "ellohay"
print(pig_latin("python"))   # Expected: "ythonpay"
print(pig_latin("is"))       # Expected: "isyay"

C.2. Write a function title_case(text) that converts a string to title case, but keeps "small words" (a, an, the, in, on, at, to, for, of, and, but, or) in lowercase unless they're the first word.

print(title_case("the lord of the rings"))
# Expected: "The Lord of the Rings"
print(title_case("a tale of two cities"))
# Expected: "A Tale of Two Cities"

C.3. Write a function word_lengths(sentence) that returns a string showing each word and its length, formatted in aligned columns.

print(word_lengths("The quick brown fox jumps"))

Expected output:

Word       Length
---------- ------
The             3
quick           5
brown           5
fox             3
jumps           5

C.4. Write a function extract_emails(text) that finds and returns a list of probable email addresses in a text. For simplicity, assume an email is any word containing exactly one @ symbol.

text = "Contact alice@example.com or bob@university.edu for details. Not an email: @@invalid"
print(extract_emails(text))
# Expected: ['alice@example.com', 'bob@university.edu']

C.5. Write a function gc_content(dna_sequence) that calculates the GC content (percentage of G and C nucleotides) of a DNA sequence. This is one of Dr. Patel's most common calculations.

print(gc_content("ATCGATCG"))     # Expected: 50.0
print(gc_content("AAAA"))         # Expected: 0.0
print(gc_content("GCGCGC"))       # Expected: 100.0
print(gc_content("ATCGAATTCCGG")) # Expected: 50.0

Part D: Input Validation and Formatting ⭐⭐⭐

D.1. Write a function validate_username(username) that checks if a username is valid. Rules: - 3-20 characters long - Only letters, digits, and underscores - Must start with a letter - Cannot end with an underscore

Return True if valid, False otherwise. Test with at least 6 different usernames (some valid, some not).

D.2. Write a function format_phone(digits) that takes a 10-digit string and returns it in (XXX) XXX-XXXX format. If the input is not exactly 10 digits, return an error message.

print(format_phone("5558675309"))     # Expected: "(555) 867-5309"
print(format_phone("123"))            # Expected: "Error: expected 10 digits, got 3"
print(format_phone("555abc1234"))     # Expected: "Error: input contains non-digit characters"

D.3. Write a program that reads a list of product names and prices, then displays them in a nicely formatted receipt:

items = [
    ("Wireless Mouse", 29.99),
    ("USB-C Hub", 49.95),
    ("Mechanical Keyboard", 149.00),
    ("Monitor Stand", 79.50),
    ("Desk Lamp", 34.99),
]

Expected output:

================================
         TECH SUPPLIES
================================
Wireless Mouse          $  29.99
USB-C Hub               $  49.95
Mechanical Keyboard     $ 149.00
Monitor Stand           $  79.50
Desk Lamp               $  34.99
--------------------------------
TOTAL (5 items)         $ 343.43
================================

D.4. Write a function password_strength(password) that evaluates password strength and returns a rating. Check for: - Length (at least 8 characters) - Contains uppercase letter - Contains lowercase letter - Contains digit - Contains special character (anything not alphanumeric)

Rating: "weak" (0-1 criteria), "fair" (2-3 criteria), "strong" (4 criteria), "very strong" (all 5).

print(password_strength("hello"))          # Expected: "weak"
print(password_strength("Hello123"))       # Expected: "fair" or "strong"
print(password_strength("H3llo!World"))    # Expected: "very strong"

Part E: Challenges ⭐⭐⭐⭐

E.1. Caesar Cipher. Write two functions: encrypt(text, shift) and decrypt(text, shift) that implement a Caesar cipher. Only shift letters (a-z, A-Z); leave spaces, digits, and punctuation unchanged. Preserve case.

print(encrypt("Hello, World!", 3))    # Expected: "Khoor, Zruog!"
print(decrypt("Khoor, Zruog!", 3))    # Expected: "Hello, World!"
print(encrypt("xyz", 3))              # Expected: "abc"  (wraps around)

Hint: Use ord() to get a character's ASCII code and chr() to convert back.

E.2. Run-Length Encoding. Write a function rle_encode(text) that compresses a string using run-length encoding: consecutive identical characters are replaced by the character followed by its count.

print(rle_encode("AAABBBCCDDDDDD"))   # Expected: "A3B3C2D6"
print(rle_encode("ABCDE"))            # Expected: "A1B1C1D1E1"
print(rle_encode("AABBA"))            # Expected: "A2B2A1"

Also write rle_decode(encoded) that reverses the process.

E.3. Text Statistics. Write a function text_stats(text) that returns a formatted report including: - Total characters (with and without spaces) - Total words - Total sentences (count ., !, ?) - Average word length - Longest word - Most common word

Test with a paragraph of text at least 50 words long.

E.4. DNA Complement. In DNA, A pairs with T and C pairs with G. Write a function reverse_complement(dna) that returns the reverse complement of a DNA sequence.

print(reverse_complement("ATCGATCG"))   # Expected: "CGATCGAT"
print(reverse_complement("AAACCC"))     # Expected: "GGGTTT"

Hint: First create the complement (A<->T, C<->G), then reverse it.

E.5. Formatted Multiplication Table. Write a function multiplication_table(n) that prints a nicely formatted multiplication table from 1 to n, with right-aligned columns.

multiplication_table(5)

Expected output:

     |    1    2    3    4    5
-----|-------------------------
   1 |    1    2    3    4    5
   2 |    2    4    6    8   10
   3 |    3    6    9   12   15
   4 |    4    8   12   16   20
   5 |    5   10   15   20   25