How many words of length n have at most k consecutive vowels?

Viewed 4666

How many words of length n have at most k consecutive vowels?

Our alphabet has 21 consonants and 5 vowels.

Please forgive me for not providing test cases. I don't have a test case because this was a phone interview problem given to a friend.

I am working on this problem from morning your little help be life saving for me. I know problem statement is vague but if you can provide some hint on this dynamic programming pattern.

I found that since it is counting problem we can do something like this dp[i][j] = length of word i with j consecutive vowel . I don't know how to proceed further .Please help in making recurrence!

5 Answers

Here is a working dynammic programming implementation:

#include <iostream>
#include <string.h>
using namespace std;

#define n 5
#define k 2

int arr[n][k + 1];

int fill(int index, int currK){
    if(arr[n][currK] != 0) return arr[n][currK];  //if already calculated
    if(index == n - 1) {                          //base case
        arr[index][currK] = 21 + (currK ? 5 : 0);
        return arr[index][currK];
    }
    
    //recursive condition and step
    if(currK == 0) arr[n][currK] = 21 * fill(index + 1, k);
    else arr[n][currK] = 5 * fill(index + 1, currK - 1) + 21 * fill(index + 1, k);
    return arr[n][currK];   
}

int main(){
    memset(arr, 0, sizeof(arr));
    cout<<fill(0, k)<<endl;

    return 0;
}

The idea is to have a matrix[n][k + 1], where matrix[i][j] stores the possibilities for a word of size i where you have used k - j consecutive vowels.

dp[i][j][k] - number of words of length i, ending with letter j and last k letters are j

base case: dp[0][0][1]=1 (index all allowed letters starting from 1)

filing table:

dp[i][j][k] = dp[i-1][j][k-1]

if(k == 1)
for j_prev in (0, mxJ):
for k_prev in (1, mxK):
dp[i][j][k]+=dp[i-1][j_prev][k_prev]

n = length. k = max consecutive values. A word of length i is valid if it has at most k consecutive values.

Let f(i,j) = number of valid words of length i ending with j vowels.

f(0,0) = 1.
f(i,0) = 21 * (sum from j=0 to min(i-1,k) of f(i-1,j)), for i>0.
f(i,j) = 5*f(i-1, j-1) for 1 <= j <= k.

Solution is sum from j = 0 to k of f(n,j).

This takes O(k) memory, and O(n*k) time. The full table is O(nk), but you only need to retain the prior row at each step, where rows are lengths as in the example below.

Sample table for n=5, k=2

           0          1          2        

0          1        n/a        n/a
1         21          5        n/a
2        546        105         25
3     14,196      2,730        525
4    366,471     70,980     13,650
5  9,473,121  1,832,355    354,900

Result: sum of final row is 11,660,376

Here is a dynamic programming solution for the same.

I have used the dp array as dp[ length_of_string ][ 2 ], where

  • dp[ i ][ 0 ] : total no of strings of length i with at most k consecutive vowels such that last character is a vowel
  • dp[ i ][ 1 ] : total no of strings of length i with at most k consecutive vowels such that last character is a consonant
Case 1: ith character is consonant

No of choices for consonant, n = 21
dp[i][1] = (n) * (total number of string of length i-1 with at most k consecutive vowels)
dp[i][1] = 21 * (dp[i-1][0] + dp[i-1][1])
Case 2: ith character is vowel

if ith character is vowel, then it can be preceded by at most k-1 vowels

No of choices for vowels, n = 5

dp[i][0] = (no of strings of length i with at most k consecutive vowels such that last character is a vowel)
              + (no of strings of length i with at most k consecutive vowels such that last 2 characters are vowels) 
              + ... + (no of strings of length i with at most k consecutive vowels such that last k characters are vowels) 

Also, 
no of strings of length i with at most k consecutive
vowels such that last p characters are vowels = (5p)*(no of strings of length i-p with at most k consecutive vowels such that last character is a consonant)

So, 
dp[i][0] = (51 * dp[i-1][1]) + (52 * dp[i-2][1]) + ... + (5k * dp[i-k][1])

Finally,

The answer will be, Count = dp[ length_of_string ][ 0 ] + dp[ length_of_string ][ 1 ]


The C++ implementation for the logic is as follows:

   #include<bits/stdc++.h>
   #define mod 1000000007
   using namespace std;

   int solve(int wordLen, int maxVowels){
       long long dp[wordLen+1][2];

       dp[0][1] = dp[0][0] = 1;
       dp[1][1] = 21;
       dp[1][0]  = 5;

       for(int i  = 2;i<=wordLen;i++){

           dp[i][1] = (21*(dp[i-1][1]+dp[i-1][0])%mod)%mod;

           int k  = i, j = 1, p = 5;
           dp[i][0] = 0;

           while(k>0 && j<=maxVowels){
               dp[i][0] = (dp[i][0] + (p*dp[i-j][1])%mod)%mod;
               p = (p*5)%mod;
               k--;
               j++;
           } 
       }

       return (int)(dp[wordLen][0]+dp[wordLen][1])%mod;
   }

   int main(){
       cout<<solve(5, 3); 
       return 0;
   }

The problem can be solved using a regular expression. You might have to insist that correctness overways pedantry if you can show an adequate solution.

This Python searches an online dictionary of words using a character count for the word length and a regexp for the n consecutive vowels. I read your task description as meaning exactly n consecutive vowels rather than at least n.

The Python

import re
import urllib


def getwords(length=5, url='http://wiki.puzzlers.org/pub/wordlists/unixdict.txt'):
    "Return lowercased words of given number of characters"
    words = urllib.request.urlopen(url).read().decode().strip().lower().split()
    return (w for w in words if len(w) ==length)

def get_special_words(length=5, consec_vowels=3):
    re_txt = r"[^aeiou\n]*([aeiou]{##CONSEC##})[^aeiou\n]*"
    regex = re.compile(re_txt.replace("##CONSEC##", str(consec_vowels)),
                       re.IGNORECASE | re.VERBOSE | re.DOTALL)
    return (w for w in getwords(length) if regex.search(w))


if __name__ == '__main__':
    wlen, consec = 7, 4
    
    print(f"Dictionary words of length {wlen} with {consec} consecutive vowels:")
    i = 0
    for i, w in enumerate(get_special_words(wlen, consec), 1):
        print(" ", w)
    print(f"\n{i} words found.")

Output

Dictionary words of length 7 with 4 consecutive vowels:
  aqueous
  sequoia

2 words found.

Ref.

The regex described.

Related