Extracting the longest sequence of letters or digits from a mixed string is a common text processing task. Whether you're parsing log files, validating input, or cleaning data, Python offers several approaches ranging from elegant regex patterns to efficient manual algorithms.
Problem Overview
Given a string containing mixed characters, the goal is to identify the longest uninterrupted sequence of alphabetic characters and the longest sequence of numeric digits.
input_text = "abc123XYZdef45gh6789"Using Regular Expressions
The re module provides the most readable solution for pattern matching tasks.
import re
def find_longest_sequences(text):
"""Find longest consecutive letter and digit sequences."""
letter_groups = re.findall(r'[a-zA-Z]+', text)
digit_groups = re.findall(r'\d+', text)
longest_letters = max(letter_groups, key=len, default='')
longest_digits = max(digit_groups, key=len, default='')
return longest_letters, longest_digits
text = "abc123XYZdef45gh6789"
letters, digits = find_longest_sequences(text)
print(f"Longest letters: '{letters}' (length {len(letters)})")
print(f"Longest digits: '{digits}' (length {len(digits)})")Output:
Longest letters: 'XYZdef' (length 6)
Longest digits: '6789' (length 4)Finding All Matches of Maximum Length
When multiple sequences share the maximum length, you might want all of them:
import re
def find_all_longest(text, pattern):
"""Return all sequences matching the maximum length."""
matches = re.findall(pattern, text)
if not matches:
return []
max_length = len(max(matches, key=len))
return [m for m in matches if len(m) == max_length]
text = "abc123XYZdef45gh6789"
longest_letters = find_all_longest(text, r'[a-zA-Z]+')
longest_digits = find_all_longest(text, r'\d+')
print(f"All longest letter sequences: {longest_letters}")
print(f"All longest digit sequences: {longest_digits}")Output:
All longest letter sequences: ['XYZdef']
All longest digit sequences: ['6789']Manual Single-Pass Algorithm
When working in environments where imports should be minimized, or when processing extremely large strings, a manual approach offers more control.
def longest_consecutive(s):
"""
Find longest letter and digit sequences in a single pass.
Time Complexity: O(n)
Space Complexity: O(k) where k is the length of longest sequence
"""
max_letters = ""
max_digits = ""
current_letters = ""
current_digits = ""
for char in s:
if char.isalpha():
current_letters += char
current_digits = "" # Reset digit tracking
if len(current_letters) > len(max_letters):
max_letters = current_letters
elif char.isdigit():
current_digits += char
current_letters = "" # Reset letter tracking
if len(current_digits) > len(max_digits):
max_digits = current_digits
else:
current_letters = ""
current_digits = ""
return max_letters, max_digits
text = "hello_world123test456789end"
letters, digits = longest_consecutive(text)
print(f"Longest letters: '{letters}'")
print(f"Longest digits: '{digits}'")Output:
Longest letters: 'hello'
Longest digits: '456789'Optimized Version Using List Building
String concatenation in loops can be inefficient. This version uses list building for better performance with very long sequences:
def longest_consecutive_optimized(s):
"""Memory-efficient version using character lists."""
max_letters = []
max_digits = []
current_letters = []
current_digits = []
for char in s:
if char.isalpha():
current_letters.append(char)
if current_digits:
current_digits = []
if len(current_letters) > len(max_letters):
max_letters = current_letters.copy()
elif char.isdigit():
current_digits.append(char)
if current_letters:
current_letters = []
if len(current_digits) > len(max_digits):
max_digits = current_digits.copy()
else:
current_letters = []
current_digits = []
return ''.join(max_letters), ''.join(max_digits)Using itertools.groupby
For a more functional approach, groupby elegantly segments the string:
from itertools import groupby
def longest_with_groupby(text):
"""Use groupby to segment string by character type."""
def char_type(c):
if c.isalpha():
return 'letter'
elif c.isdigit():
return 'digit'
return 'other'
longest_letters = ""
longest_digits = ""
for key, group in groupby(text, key=char_type):
segment = ''.join(group)
if key == 'letter' and len(segment) > len(longest_letters):
longest_letters = segment
elif key == 'digit' and len(segment) > len(longest_digits):
longest_digits = segment
return longest_letters, longest_digits
text = "test123_example4567_data89"
letters, digits = longest_with_groupby(text)
print(f"Longest letters: '{letters}'")
print(f"Longest digits: '{digits}'")Output:
Longest letters: 'example'
Longest digits: '4567'Handling Unicode Characters
For international text, consider Unicode letter categories:
import re
def longest_unicode_sequences(text):
"""Handle Unicode letters and digits."""
letter_pattern = r'[^\W\d_]+' # Letters only
digit_pattern = r'\d+'
letters = re.findall(letter_pattern, text, re.UNICODE)
digits = re.findall(digit_pattern, text)
return (
max(letters, key=len, default=''),
max(digits, key=len, default='')
)
text = "héllo123wörld456日本語789"
letters, digits = longest_unicode_sequences(text)
print(f"Longest letters: '{letters}'")
print(f"Longest digits: '{digits}'")Output:
Longest letters: 'héllo'
Longest digits: '123'Method Comparison
| Method | Time Complexity | Space Complexity | Best For |
|---|---|---|---|
| Regex | O(n) | O(n) | Readability, most use cases |
| Manual | O(n) | O(k) | Memory constraints, streaming |
| groupby | O(n) | O(n) | Functional programming style |
| Unicode regex | O(n) | O(n) | International text |
Choose regex for typical applications where code clarity matters. Switch to the manual approach when processing very large files or when you need precise memory control.