Refactor string methods for StringArray + return IntegerArray for numeric results
Intended as an alternative to https://github.com/pandas-dev/pandas/pull/29637.

This is a much smaller change. It only changes the codepath for StringDtype.
I think this is OK since someday (well down the road) we'll want to deprecate
the `.str` accessor on object-dtype Series. When we enforce that, we can just
delete the entire old implementation.

The API change is limited to always returning Int64Dtype for numeric outputs, rather than int if there's no NAs and float if there are any.
When BoolArray is done, we'll change that for the boolean-returning ones.

As a side benefit, we get a nice perf boost, since we have deterministic output dtypes we
can skip an object-dtype allocation.

```python
In [2]: s = pd.Series(['a'] * 100_000 + [None], dtype="string")

In [3]: t = s.astype(object)

In [4]: %timeit s.str.count("a")
50.7 ms ± 2.21 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)


In [5]: %timeit t.str.count("a")
62.7 ms ± 742 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
```