Here is an optimised pattern that checks strictly the format you describe:
^(?=\S)(?:[1-9][0-9]*d)?(?:(?:^| )(?:1[0-9]?|[3-9]|2[0-3]?)h)?(?:(?:^| )(?:[1-5][0-9]?|[6-9])m)?$
Details:
(?=\S)
ensures that there is at least one group in the pattern and forces the string to not begin with a space. (note that you only need to find one character)
[1-9][0-9]*d
is faster than (?:[1-9]|[1-9][0-9]+)d
since the character class [1-9]
is tested only once.
(?:1[0-9]?|[3-9]|2[0-3]?)
a bit longer than (?:[1-9]|1[0-9]|2[0-3])
but more efficient since the first digit is tested only once. Alternatives are sorted by probability (11/22 for the first, 7/22 for the second, 4/22 for the last)
The same for [1-5][0-9]?|[6-9]
.
I have assumed that groups must be separated with a space. To ensure that, I have removed \s?
at the end of the groups 1 and 2 to put (?:^| )
at the begining of the groups 2 and 3. There is only two possibilities: the group is at the begining or there is an other group before and a space (remember that you can't have a space and no group before due to the lookahead)
You obtain in fine a pattern a little longer (one character) but faster and more binding. If you need for an obscure reason to reduce the size of the pattern, you can gain 3 characters using the old subpatterns for numbers in group 2 and 3.