<p>You are given a <strong>0-indexed</strong> binary string <code>s</code> which represents the types of buildings along a street where:</p>

<ul>
	<li><code>s[i] = &#39;0&#39;</code> denotes that the <code>i<sup>th</sup></code> building is an office and</li>
	<li><code>s[i] = &#39;1&#39;</code> denotes that the <code>i<sup>th</sup></code> building is a restaurant.</li>
</ul>

<p>As a city official, you would like to <strong>select</strong> 3 buildings for random inspection. However, to ensure variety, <strong>no two consecutive</strong> buildings out of the <strong>selected</strong> buildings can be of the same type.</p>

<ul>
	<li>For example, given <code>s = &quot;0<u><strong>0</strong></u>1<u><strong>1</strong></u>0<u><strong>1</strong></u>&quot;</code>, we cannot select the <code>1<sup>st</sup></code>, <code>3<sup>rd</sup></code>, and <code>5<sup>th</sup></code> buildings as that would form <code>&quot;0<strong><u>11</u></strong>&quot;</code> which is <strong>not</strong> allowed due to having two consecutive buildings of the same type.</li>
</ul>

<p>Return <em>the <b>number of valid ways</b> to select 3 buildings.</em></p>

<p>&nbsp;</p>
<p><strong class="example">Example 1:</strong></p>

<pre>
<strong>Input:</strong> s = &quot;001101&quot;
<strong>Output:</strong> 6
<strong>Explanation:</strong> 
The following sets of indices selected are valid:
- [0,2,4] from &quot;<u><strong>0</strong></u>0<strong><u>1</u></strong>1<strong><u>0</u></strong>1&quot; forms &quot;010&quot;
- [0,3,4] from &quot;<u><strong>0</strong></u>01<u><strong>10</strong></u>1&quot; forms &quot;010&quot;
- [1,2,4] from &quot;0<u><strong>01</strong></u>1<u><strong>0</strong></u>1&quot; forms &quot;010&quot;
- [1,3,4] from &quot;0<u><strong>0</strong></u>1<u><strong>10</strong></u>1&quot; forms &quot;010&quot;
- [2,4,5] from &quot;00<u><strong>1</strong></u>1<u><strong>01</strong></u>&quot; forms &quot;101&quot;
- [3,4,5] from &quot;001<u><strong>101</strong></u>&quot; forms &quot;101&quot;
No other selection is valid. Thus, there are 6 total ways.
</pre>

<p><strong class="example">Example 2:</strong></p>

<pre>
<strong>Input:</strong> s = &quot;11100&quot;
<strong>Output:</strong> 0
<strong>Explanation:</strong> It can be shown that there are no valid selections.
</pre>

<p>&nbsp;</p>
<p><strong>Constraints:</strong></p>

<ul>
	<li><code>3 &lt;= s.length &lt;= 10<sup>5</sup></code></li>
	<li><code>s[i]</code> is either <code>&#39;0&#39;</code> or <code>&#39;1&#39;</code>.</li>
</ul>

class Solution:
    def numberOfWays(self, s: str) -> int:
        # the 3 buildings can only be 101 or 010
        # hence we can just find the number of 1's and the number of 0's and compute the possibility as:
        # 101 -> num_ones * (num_ones -1) // 2 * num_zeros
        # ...

        # above did not consider the repeated 1's and 0's

        # we should handle both cases separately
        # seems like a dynamic programming problem
        # given the first number
        # we want to find a second number that is different from the first one
        #   we want to count the number of possibilities
        # and a third number that is different from the second one
        
        # so for each element, we want to compute the number of different next elements after this one
        # locate 0's and 1's
        # ht = {"0": [], "1": []}
        # val_diff = [0] * len(s) # the first element is not needed
        # val_0 = [0] * len(s)
        # val_1 = [0] * len(s) 
        # if s[0] == "0":
        #     val_0[0] += 1
        # else:
        #     val_1[0] += 1
        # for i in range(1, len(s)):
        #     # increment the val_diff for previous values
        #     for j in ht[str(1-int(s[i]))]:
        #         val_diff[j] += 1
        #     ht[s[i]].append(i)
        #     # increment the val_0/1's
        #     if s[i] == "0":
        #         val_0[i] = val_0[i-1] + 1
        #         val_1[i] = val_1[i-1]
        #     else:
        #         val_0[i] = val_0[i-1]
        #         val_1[i] = val_1[i-1] + 1
        # # print(f"val_diff = {val_diff}, val_0 = {val_0}, val_1 = {val_1}")
        # ans = 0
        # for i in range(1, len(s)):
        #     if s[i] == "0":
        #         ans += val_1[i-1] * val_diff[i]
        #     else:
        #         ans += val_0[i-1] * val_diff[i]
        #     # print(f"{s[i]}: {ans}")
        # return ans

        # this is also not the best solution
        # the best one is to compute at each location the num of 0's and 1's before and after this point
        val_0 = [0] * len(s)
        val_0_r = [0] * len(s)
        val_1 = [0] * len(s)
        val_1_r = [0] * len(s)
        for i in range(len(s)):
            if s[i] == "0":
                if i == 0:
                    val_0[0] += 1
                else:
                    val_0[i] = val_0[i-1] + 1
                    val_1[i] = val_1[i-1]
            else:
                if i == 0:
                    val_1[0] += 1
                else:
                    val_0[i] = val_0[i-1]
                    val_1[i] = val_1[i-1] + 1

            j = len(s)-1-i
            if s[j] == "0":
                if j == (len(s)-1):
                    val_0_r[j] += 1
                else:
                    val_0_r[j] = val_0_r[j+1] + 1
                    val_1_r[j] = val_1_r[j+1]
            else:
                if j == (len(s)-1):
                    val_1_r[j] += 1
                else:
                    val_0_r[j] = val_0_r[j+1]
                    val_1_r[j] = val_1_r[j+1] + 1
            # print(f"{j}: 0_r = {val_0_r}, 1_r = {val_1_r}")
        # print(f"val_0 = {val_0}/{val_0_r}, val_1 = {val_1}/{val_1_r}")
        ans = 0
        for i in range(1, len(s)-1):
            if s[i] == "0":
                ans += val_1[i-1] * val_1_r[i+1]
                # print(f"{s[i]}: {ans}/{val_1[i-1]}/{val_1_r[i+1]}")
            else:
                ans += val_0[i-1] * val_0_r[i+1]
                # print(f"{s[i]}: {ans}/{val_0[i-1]}/{val_0_r[i+1]}")
        return ans


1205