Given a string str and an array of strings dictionary[], the task is to find the number of ways str can be formed as a concatenation of strings (any number of times) in a dictionary[].
Examples:
Input: str = abab, dictionary[] = { a, b, ab } Output: 4 Explanation: There are 4 ways to form string str
- a + b + a + b
- ab + a + b
- a + b + ab
- ab + ab
Input: str = geeks, dictionary[] = { geeks, for, geek } Output: 1
Approach: The problem can be solved based on the following idea:
Use Trie data structure to store the strings of a given set in reverse order and count[] where count(i) denotes the number of ways to form string str[0….i] For every index i, if str[ j…….i ] is found in trie => count(i) += count(j – 1), j: from i to 0
Follow the steps mentioned below to implement the idea:
- Reverse the strings of a given set dictionary[], and insert them into a trie.
- Run loop i: from 0 to N – 1 for count[], where count(i) denotes the number of ways to form string str[0….i].
- Run loop j: from i to 0, conceptually denotes a temporary string str[j….i]
- Correspondingly maintain the pointer ptr which denotes that prefix str[j+1….i] is found in a trie.
- character ch = s[j],
- if( ptr -> children[ch – ‘a’] == nullptr) it means str[j…i] is not found and break the loop.
- if( ptr -> endOfWord == true) count(i) += count(j – 1).
- Return count(n – 1) as an answer.
Below is the implementation of the above approach:
C++
#include <bits/stdc++.h>
using namespace std;
const int alphabet_size = 26;
struct Trie {
bool endOfWord = false ;
Trie* children[alphabet_size];
Trie()
{
for ( int i = 0; i < alphabet_size; i++)
children[i] = nullptr;
}
};
Trie* root;
void insert(string s)
{
int n = s.size();
Trie* prev = root;
for ( int i = 0; i < n; i++) {
if (prev->children[s[i] - 'a' ] == nullptr) {
Trie* temp = new Trie;
prev->children[s[i] - 'a' ] = temp;
}
prev = prev->children[s[i] - 'a' ];
}
prev->endOfWord = true ;
}
int waysOfFormingString(string& str)
{
int n = str.size();
vector< int > count(n, 0);
for ( int i = 0; i < n; i++) {
Trie* ptr = root;
for ( int j = i; j >= 0; j--) {
char ch = str[j];
if (ptr->children[ch - 'a' ] == nullptr)
break ;
ptr = ptr->children[ch - 'a' ];
if (ptr->endOfWord == true )
count[i] += j > 0 ? count[j - 1] : 1;
}
}
return count[n - 1];
}
int main()
{
string str = "abab" ;
string dictionary[] = { "a" , "b" , "ab" };
int m = 3;
root = new Trie;
for ( int i = 0; i < m; i++) {
reverse(dictionary[i].begin(), dictionary[i].end());
insert(dictionary[i]);
}
cout << waysOfFormingString(str) << endl;
return 0;
}
|
Java
import java.io.*;
class TrieNode {
public boolean endOfWord = false ;
public TrieNode[] children = new TrieNode[ 26 ];
}
class Trie {
private TrieNode root = new TrieNode();
public void insert(String s)
{
TrieNode prev = root;
for ( char c : s.toCharArray()) {
int index = c - 'a' ;
if (prev.children[index] == null ) {
prev.children[index] = new TrieNode();
}
prev = prev.children[index];
}
prev.endOfWord = true ;
}
public int waysOfFormingString(String str)
{
int n = str.length();
int [] count = new int [n];
for ( int i = 0 ; i < n; i++) {
TrieNode ptr = root;
for ( int j = i; j >= 0 ; j--) {
char ch = str.charAt(j);
int index = ch - 'a' ;
if (ptr.children[index] == null ) {
break ;
}
ptr = ptr.children[index];
if (ptr.endOfWord)
{
count[i] += j > 0 ? count[j - 1 ] : 1 ;
}
}
}
return count[n - 1 ];
}
}
class GFG {
public static void main(String[] args)
{
String str = "abab" ;
String[] dictionary = { "a" , "b" , "ab" };
int m = dictionary.length;
Trie trie = new Trie();
for (String s : dictionary) {
char [] arr = s.toCharArray();
StringBuilder sb
= new StringBuilder( new String(arr));
trie.insert(sb.reverse().toString());
}
System.out.println(trie.waysOfFormingString(str));
}
}
|
Python3
class Trie:
def __init__( self ):
self .endOfWord = False
self .children = [ None ] * 26
def insert(root, s):
n = len (s)
prev = root
for i in range (n):
index = ord (s[i]) - ord ( 'a' )
if prev.children[index] is None :
prev.children[index] = Trie()
prev = prev.children[index]
prev.endOfWord = True
def waysOfFormingString(root, s):
n = len (s)
count = [ 0 ] * n
for i in range (n):
ptr = root
for j in range (i, - 1 , - 1 ):
ch = s[j]
index = ord (ch) - ord ( 'a' )
if ptr.children[index] is None :
break
ptr = ptr.children[index]
if ptr.endOfWord:
if j > 0 :
count[i] + = count[j - 1 ]
else :
count[i] + = 1
return count[n - 1 ]
if __name__ = = '__main__' :
str = "abab"
dictionary = [ "a" , "b" , "ab" ]
m = 3
root = Trie()
for i in range (m):
insert(root, dictionary[i][:: - 1 ])
print (waysOfFormingString(root, str ))
|
C#
using System;
public class TrieNode {
public bool endOfWord = false ;
public TrieNode[] children = new TrieNode[26];
}
public class Trie {
private TrieNode root = new TrieNode();
public void Insert( string s)
{
TrieNode prev = root;
foreach ( char c in s)
{
int index = c - 'a' ;
if (prev.children[index] == null )
prev.children[index] = new TrieNode();
prev = prev.children[index];
}
prev.endOfWord = true ;
}
public int WaysOfFormingString( string str)
{
int n = str.Length;
int [] count = new int [n];
for ( int i = 0; i < n; i++) {
TrieNode ptr = root;
for ( int j = i; j >= 0; j--) {
char ch = str[j];
int index = ch - 'a' ;
if (ptr.children[index] == null )
break ;
ptr = ptr.children[index];
if (ptr.endOfWord)
count[i] += j > 0 ? count[j - 1] : 1;
}
}
return count[n - 1];
}
}
public class Program {
public static void Main()
{
string str = "abab" ;
string [] dictionary = { "a" , "b" , "ab" };
int m = dictionary.Length;
Trie trie = new Trie();
foreach ( string s in dictionary)
{
char [] arr = s.ToCharArray();
Array.Reverse(arr);
trie.Insert( new string (arr));
}
Console.WriteLine(trie.WaysOfFormingString(str));
}
}
|
Javascript
const alphabet_size = 26;
class Trie {
constructor() {
this .endOfWord = false ;
this .children = Array(alphabet_size).fill( null );
}
}
let root;
function insert(s) {
let n = s.length;
let prev = root;
for (let i = 0; i < n; i++) {
if (prev.children[s[i].charCodeAt() - 'a' .charCodeAt()] === null ) {
let temp = new Trie();
prev.children[s[i].charCodeAt() - 'a' .charCodeAt()] = temp;
}
prev = prev.children[s[i].charCodeAt() - 'a' .charCodeAt()];
}
prev.endOfWord = true ;
}
function waysOfFormingString(str) {
let n = str.length;
let count = Array(n).fill(0);
for (let i = 0; i < n; i++) {
let ptr = root;
for (let j = i; j >= 0; j--) {
let ch = str[j];
if (ptr.children[ch.charCodeAt() - 'a' .charCodeAt()] === null ) break ;
ptr = ptr.children[ch.charCodeAt() - 'a' .charCodeAt()];
if (ptr.endOfWord === true ) count[i] += j > 0 ? count[j - 1] : 1;
}
}
return count[n - 1];
}
function main() {
let str = "abab" ;
let dictionary = [ "a" , "b" , "ab" ];
let m = 3;
root = new Trie();
for (let i = 0; i < m; i++) {
dictionary[i] = dictionary[i].split( "" ).reverse().join( "" );
insert(dictionary[i]);
}
console.log(waysOfFormingString(str));
}
main();
|
Time Complexity: O(N * N) Auxiliary Space: O(M)
Another Approach: The problem can be solved based on the following idea:
Using polynomial hashing, the hash value of any substring of a string str can be calculated in O(1) time after an O(n) time preprocessing.
The fact that there are at most O(?m) distinct string lengths if total length of all strings in dictionary[] is m.
count[] where count(i) denotes the number of ways to form string str[0….i]
For every index i,
if str[ j…….i ] is found in diciionary[] => count(i) += count(j – 1), j: from i to 0
Follow the steps mentioned below to implement the idea:
- /archive/string-hashing-using-polynomial-rolling-hash-function/
- Construct a map of set hash_grp that contains all hash values of the strings in the dictionary[] grouped according to the string’s length.
- Construct an array hashed_arr[] that stores the hashed values of all prefixes of string str.
- When calculating a value of count(i), we go through all values of len such that there is a string of length len in the dictionary[].
- calculate the hash value of s[i ? len +1………i] and check if it belongs to hashed_grp[len].
- hashed_arr[j……..i] * pj = hashed_arr[0……i] – hashed_arr[0……j – 1], where p is an arbitrary constant integer.
- count(i) += count(j – 1).
- Return count(n – 1) as an answer.
Below is the implementation of the above approach:
C++
#include <bits/stdc++.h>
using namespace std;
long long p = 31, m = 1e9 + 7;
int power( int x, int y, int mod)
{
int res = 1;
while (y > 0) {
if (y % 2 == 1)
res = (res * x)%mod;
y = y >> 1;
x = (x * x)%mod;
}
return res % mod;
}
int get_hash(string &s) {
long long hash = 0;
long long p_pow = 1;
int n = s.size();
for ( int i = 0; i < n; i++) {
hash = ((hash + (s[i] - 'a' + 1) * p_pow) % m);
p_pow = (p_pow * p) % m;
}
return hash;
}
vector< int > hashing(string &s)
{
int n = s.size();
vector< int > hash(n, 0);
hash[0] = s[0] - 'a' + 1;
long long p_pow = p;
for ( int i = 1; i < n; i++) {
hash[i] = ( int )((hash[i - 1] + (s[i] - 'a' + 1) * p_pow) % m);
p_pow = (p_pow * p) % m;
}
return hash;
}
int waysOfFormingString(string s, vector<string> &dic)
{
int n = s.size();
int k = dic.size();
map< int , set< int >> hash_grp;
for ( int i = 0; i < k; i++)
{
int p = get_hash(dic[i]);
hash_grp[dic[i].size()].insert(p);
}
vector< int > hashed_arr = hashing(s);
vector< int > count(n, 0);
for ( int i = 0; i < n; i++)
{
for ( auto x : hash_grp)
{
int len = x.first;
if (i + 1 < len) break ;
int p_pow = power(p, i - len + 1, m);
int hashed_value = (i+1 != len) ?
((hashed_arr[i] - hashed_arr[i - len]) / p_pow) :
(hashed_arr[i] / p_pow);
if ((x.second).find(hashed_value) != (x.second).end())
count[i] += (i + 1 != len) ? count[i - len] : 1;
}
}
return count[n - 1];
}
int main()
{
string str = "abab" ;
vector<string> dictionary = { "a" , "b" , "ab" };
cout << waysOfFormingString(str, dictionary) << endl;
return 0;
}
|
Java
import java.util.*;
public class WaysOfFormingString {
static long p = 31 , m = 1000000007 ;
static long power( long x, long y, long mod) {
long res = 1 ;
while (y > 0 ) {
if ((y & 1 ) == 1 ) {
res = (res * x) % mod;
}
x = (x * x) % mod;
y >>= 1 ;
}
return res;
}
static long getHash(String s) {
long hash = 0 , pPow = 1 ;
for ( int i = 0 ; i < s.length(); i++) {
hash = (hash + (s.charAt(i) - 'a' + 1 ) * pPow) % m;
pPow = (pPow * p) % m;
}
return hash;
}
static List<Long> hashing(String s) {
int n = s.length();
List<Long> hash = new ArrayList<>(n);
hash.add(( long )(s.charAt( 0 ) - 'a' + 1 ));
long pPow = p;
for ( int i = 1 ; i < n; i++) {
hash.add((hash.get(i - 1 ) + (s.charAt(i) - 'a' + 1 ) * pPow) % m);
pPow = (pPow * p) % m;
}
return hash;
}
static int waysOfFormingString(String s, List<String> dict) {
int n = s.length(), k = dict.size();
Map<Integer, Set<Long>> hashGrp = new HashMap<>();
for (String word : dict) {
long p = getHash(word);
hashGrp.computeIfAbsent(word.length(), k1 -> new HashSet<>()).add(p);
}
List<Long> hashedArr = hashing(s);
int [] count = new int [n];
for ( int i = 0 ; i < n; i++) {
for (Map.Entry<Integer, Set<Long>> entry : hashGrp.entrySet()) {
int len = entry.getKey();
if (i + 1 < len) {
break ;
}
long pPow = power(p, i - len + 1 , m);
long hashedValue = (i + 1 != len) ? ((hashedArr.get(i) - hashedArr.get(i - len)) / pPow)
: (hashedArr.get(i) / pPow);
if (entry.getValue().contains(hashedValue)) {
count[i] += (i + 1 != len) ? count[i - len] : 1 ;
}
}
}
return count[n - 1 ];
}
public static void main(String[] args) {
String str = "abab" ;
List<String> dictionary = Arrays.asList( "a" , "b" , "ab" );
System.out.println(waysOfFormingString(str, dictionary));
}
}
|
Python3
p = 31
m = 10 * * 9 + 7
def power(x, y, mod):
res = 1
while y > 0 :
if y % 2 = = 1 :
res = (res * x) % mod
y = y / / 2
x = (x * x) % mod
return res % mod
def get_hash(s):
hash = 0
p_pow = 1
n = len (s)
for i in range (n):
hash = ( hash + ( ord (s[i]) - ord ( 'a' ) + 1 ) * p_pow) % m
p_pow = (p_pow * p) % m
return hash
def hashing(s):
n = len (s)
hash = [ 0 ] * n
hash [ 0 ] = ord (s[ 0 ]) - ord ( 'a' ) + 1
p_pow = p
for i in range ( 1 , n):
hash [i] = ( hash [i - 1 ] + ( ord (s[i]) - ord ( 'a' ) + 1 ) * p_pow) % m
p_pow = (p_pow * p) % m
return hash
def waysOfFormingString(s, dic):
n = len (s)
k = len (dic)
hash_grp = {}
for word in dic:
h = get_hash(word)
hash_grp.setdefault( len (word), set ()).add(h)
hashed_arr = hashing(s)
count = [ 0 ] * n
for i in range (n):
for length, hash_set in hash_grp.items():
if i + 1 < length:
break
p_pow = power(p, i - length + 1 , m)
hashed_value = (hashed_arr[i] - hashed_arr[i - length]) / / p_pow if i + 1 ! = length else hashed_arr[i] / / p_pow
if hashed_value in hash_set:
count[i] + = count[i - length] if i + 1 ! = length else 1
return count[n - 1 ]
if __name__ = = "__main__" :
str = "abab"
dictionary = [ "a" , "b" , "ab" ]
print (waysOfFormingString( str , dictionary))
|
C#
using System;
using System.Collections.Generic;
namespace ConsoleApp
{
class Gfg
{
static long p = 31, m = 1000000007;
static int power( int x, int y, int mod)
{
int res = 1;
while (y > 0)
{
if (y % 2 == 1)
res = ( int )((res * ( long )x) % mod);
y = y >> 1;
x = ( int )((x * ( long )x) % mod);
}
return res % mod;
}
static int get_hash( string s)
{
long hash = 0;
long p_pow = 1;
int n = s.Length;
for ( int i = 0; i < n; i++)
{
hash = ((hash + (s[i] - 'a' + 1) * p_pow) % m);
p_pow = (p_pow * p) % m;
}
return ( int )hash;
}
static List< int > hashing( string s)
{
int n = s.Length;
List< int > hash = new List< int >(n);
hash.Add(s[0] - 'a' + 1);
long p_pow = p;
for ( int i = 1; i < n; i++)
{
hash.Add(( int )((hash[i - 1] + (s[i] - 'a' + 1) * p_pow) % m));
p_pow = (p_pow * p) % m;
}
return hash;
}
static int waysOfFormingString( string s, List< string > dic)
{
int n = s.Length;
int k = dic.Count;
Dictionary< int , HashSet< int >> hashGrp = new Dictionary< int , HashSet< int >>();
for ( int i = 0; i < k; i++)
{
int p = get_hash(dic[i]);
if (!hashGrp.ContainsKey(dic[i].Length))
hashGrp[dic[i].Length] = new HashSet< int >();
hashGrp[dic[i].Length].Add(p);
}
List< int > hashedArr = hashing(s);
List< int > count = new List< int >( new int [n]);
for ( int i = 0; i < n; i++)
{
foreach (KeyValuePair< int , HashSet< int >> x in hashGrp)
{
int len = x.Key;
if (i + 1 < len) break ;
int p_pow = power(( int )p, i - len + 1, ( int )m);
int hashedValue = (i + 1 != len) ?
((hashedArr[i] - hashedArr[i - len]) / p_pow) :
(hashedArr[i] / p_pow);
if (x.Value.Contains(hashedValue))
count[i] += (i + 1 != len) ? count[i - len] : 1;
}
}
return count[n - 1];
}
static void Main( string [] args)
{
string str = "abab" ;
List< string > dictionary = new List< string > { "a" , "b" , "ab" };
Console.WriteLine(waysOfFormingString(str, dictionary));
}
}
}
|
Javascript
function power(x, y, mod) {
let res = 1;
while (y > 0) {
if (y % 2 == 1) {
res = (res * x) % mod;
}
y = Math.floor(y / 2);
x = (x * x) % mod;
}
return res % mod;
}
function getHash(s) {
const p = 31;
const m = 1000000007;
let hash = 0;
let pPow = 1;
const n = s.length;
for (let i = 0; i < n; i++) {
hash = ((hash + (s.charCodeAt(i) - 'a' .charCodeAt(0) + 1) * pPow) % m);
pPow = (pPow * p) % m;
}
return hash;
}
function hashing(s) {
const p = 31;
const m = 1000000007;
const n = s.length;
const hash = new Array(n);
hash[0] = s.charCodeAt(0) - 'a' .charCodeAt(0) + 1;
let pPow = p;
for (let i = 1; i < n; i++) {
hash[i] = ((hash[i - 1] + (s.charCodeAt(i) - 'a' .charCodeAt(0) + 1) * pPow) % m);
pPow = (pPow * p) % m;
}
return hash;
}
function waysOfFormingString(s, dic) {
const p = 31;
const m = 1000000007;
const n = s.length;
const k = dic.length;
const hashGrp = new Map();
for (let i = 0; i < k; i++) {
const p = getHash(dic[i]);
if (!hashGrp.has(dic[i].length)) {
hashGrp.set(dic[i].length, new Set());
}
hashGrp.get(dic[i].length).add(p);
}
const hashedArr = hashing(s);
const count = new Array(n).fill(0);
for (let i = 0; i < n; i++) {
for (const [len, hashValues] of hashGrp) {
if (i + 1 < len) {
break ;
}
const pPow = power(p, i - len + 1, m);
const hashedValue = (i + 1 != len) ?
((hashedArr[i] - hashedArr[i - len]) / pPow) :
(hashedArr[i] / pPow);
if (hashValues.has(hashedValue)) {
count[i] += (i + 1 != len) ? count[i - len] : 1;
}
}
}
return count[n - 1];
}
const str = "abab" ;
const dictionary = [ "a" , "b" , "ab" ];
console.log(waysOfFormingString(str, dictionary));
|
Time Complexity: O(N * ?M)
Auxiliary Space: O(N + ?M)
M is the total length of all strings in a set and N is the length of a given string.
|