Maximal Char Requests in a String
PEP 8

The Style Guide for Python Code’s naming conventions recommends again mixedCase identifiers.  CapWords are for classes and types, snake_case is for functions, methods and variables.  Thus, getMaxCharCount, maxCount and finalWord should be renamed to get_max_char_count, max_count and final_word.

string

string is an importable module in Python.  Its use as an identifier should be discouraged.

Intervals

interval is a perfectly fine variable, but interval[0] and interval[1] are confusing.  Are those the zeroth and first intervals in a list of intervals?  It would be better to unpack the interval immediately into the starting and ending values of the interval.  This can even be done directly by the for statement:

    for start, end in queries:
        if start == end:
            ...


Special Casing

You have special-cased the length 1 interval.  Why?  Does the general purpose version of the code not work for the length one interval?  Is it too slow?  Is the special-case a common occurrence, or does checking for it actually slow down the general purpose case be introducing an unnecessary if statement?

List Comprehension

If we remove the special-case, your code reads approximately:

    max_count = []
    for start, end in queries:
        max_count.append(...)


This pattern is usually re-written using list-comprehension, since it is much faster than repeated calls to .append():

    max_count = [ ... for start, end in queries ]


The only issue is what should be used in place of ....  Prior to Python 3.7, there was no easy way to refer to the result of a computation multiple times in one expression.  Python 3.8 introduces the “walrus operator” (:=) which allows variables to be assigned inside of other expressions.  We can use this to avoid referencing and slicing final_word twice:

    max_count = [ (span := final_word[start:end+1]).count(max(span)) for start, end in queries ]


Reworked Code (Python 3.8)

def get_max_char_count(s, queries):
    final_word = s.lower()
    return [(span := final_word[start:end+1]).count(max(span)) for start, end in queries]


This code may be slightly faster than your original code, with the speed up mostly from replacing .append with list comprehension.  To really see significant speed gains, you need to improve the algorithm.

Algorithmic Improvement

Since this is a coding challenge, I won’t give you code for the better algorithm, but I will get you started in the right direction.

How many times are you looking at any given character of final_word?

Consider the following test case:

2000
AAAAAAAAAAA...[2000 A’s] ... AAAAAAAAAAAA
2000
0 1999
0 1999
0 1999
  :      :
[2000 duplicate lines]
  :      :
0 1999
0 1999


The result should be a list of 2000 copies of the number 2000.  We did this in our head.  Your algorithm will test each character 4000 times; 2000 times during max and another 2000 times during count.  Clearly, we’ve got room for improvement.  The ideal would be if each character was only tested twice, once during max, and once during count.  But how can we achieve this?

Consider another test case:


       20
     AAAABBBBCCCCBBBBCCCC
     3
     0 11
     4 15
     8 19  


Can you see any way of computing intermediate results and reusing them?


   Can you divide this into 5 subproblems?  Can you combine the first 3 subproblems together to get the first answer?  4 A’s, 4 B’s and 4 C’s makes 4 C’s.  Subproblems 2 to 4 to get the second answer?  4 B’s, 4 C’s and 4 B’s makes 4 C’s.  Subproblems 3 to 5 to get the 3rd answer?  4 C’s, 4 B’s and 4 C’s makes 8 C’s.  How would you store the subproblems results?  Where did we get the boundaries of the subproblems?