Search a list of keywords [closed] - string

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Questions asking for code must demonstrate a minimal understanding of the problem being solved. Include attempted solutions, why they didn't work, and the expected results. See also: Stack Overflow question checklist
Closed 9 years ago.
Improve this question
I'm building a messaging anti-spam solution where I have to compare every text message I receive to a list of keywords and if the text message has one of the keywords in list i have to drop it.
The question is what is the best algorithm to search a list of keywords? example is below
text message received is "hi how are you, visit us at www.xyz.com"
and the list sample is below
www.abc.com
www.xyz.com
...
...

If there are many keywords, especially having common prefixes, a trie might work well here.
I'll assume you want substring, not just words, i.e. given a keyword bah, it will find bah in bahama. Modifying this to prevent this shouldn't be difficult.
I also assume you don't have a keyword and it's substring as a keyword (i.e. bah and bahama can't both be keywords). Catering for this also shouldn't be too difficult.
Just, for each character in the string, start searching at the top of the tree and continue searching each existing pointer in the tree. Once one of the pointers reaches a valid word, do as you wish with it and probably remove all pointers in the tree.
Complexity:
O(max(n2, mn)) where m is the number of nodes in the tree, in the worst case, although average case performance should be a lot better.
Example:
So, let's say we have keywords:
ab
b
caa
We might get a tree like:
o
/|\
a / | \ c
/ |b \
o o o
| b | a
o o
| a
o
(o is just a node)
Now, for an input string caab, we first look at c: (x indicates a pointer in the tree)
o
/|\
a / | \ c
/ |b \
o o x
| b | a
o o
| a
o
Note the new pointer on the right.
Then a:
o
/|\
a / | \ c
/ |b \
x o o
| b | a
o x
| a
o
Note the new pointer on the left and the one on the right advanced.
Then a:
o
/|\
a / | \ c
/ |b \
o o o
| b | a
o o
| a
x
Note the pointer on the left disappeared and the one on the right advanced.
Now we remove the one on the right since we found a valid word.
Then b:
o
/|\
a / | \ c
/ |b \
o x o
| b | a
o o
| a
o
Note the new pointer in the middle, which we then also remove because we found a valid word.

How many keywords are you talking about? Look into the Boyer-Moore String Search algorithm, it might work well for your purposes and it's not difficult to implement. Here's a java implementation taken from the wikipedia article:
/**
* Returns the index within this string of the first occurrence of the
* specified substring. If it is not a substring, return -1.
*
* #param haystack The string to be scanned
* #param needle The target string to search
* #return The start index of the substring
*/
public static int indexOf(char[] haystack, char[] needle) {
if (needle.length == 0) {
return 0;
}
int charTable[] = makeCharTable(needle);
int offsetTable[] = makeOffsetTable(needle);
for (int i = needle.length - 1, j; i < haystack.length;) {
for (j = needle.length - 1; needle[j] == haystack[i]; --i, --j) {
if (j == 0) {
return i;
}
}
// i += needle.length - j; // For naive method
i += Math.max(offsetTable[needle.length - 1 - j], charTable[haystack[i]]);
}
return -1;
}
/**
* Makes the jump table based on the mismatched character information.
*/
private static int[] makeCharTable(char[] needle) {
final int ALPHABET_SIZE = 256;
int[] table = new int[ALPHABET_SIZE];
for (int i = 0; i < table.length; ++i) {
table[i] = needle.length;
}
for (int i = 0; i < needle.length - 1; ++i) {
table[needle[i]] = needle.length - 1 - i;
}
return table;
}
/**
* Makes the jump table based on the scan offset which mismatch occurs.
*/
private static int[] makeOffsetTable(char[] needle) {
int[] table = new int[needle.length];
int lastPrefixPosition = needle.length;
for (int i = needle.length - 1; i >= 0; --i) {
if (isPrefix(needle, i + 1)) {
lastPrefixPosition = i + 1;
}
table[needle.length - 1 - i] = lastPrefixPosition - i + needle.length - 1;
}
for (int i = 0; i < needle.length - 1; ++i) {
int slen = suffixLength(needle, i);
table[slen] = needle.length - 1 - i + slen;
}
return table;
}
/**
* Is needle[p:end] a prefix of needle?
*/
private static boolean isPrefix(char[] needle, int p) {
for (int i = p, j = 0; i < needle.length; ++i, ++j) {
if (needle[i] != needle[j]) {
return false;
}
}
return true;
}
/**
* Returns the maximum length of the substring ends at p and is a suffix.
*/
private static int suffixLength(char[] needle, int p) {
int len = 0;
for (int i = p, j = needle.length - 1;
i >= 0 && needle[i] == needle[j]; --i, --j) {
len += 1;
}
return len;
}

Related

Number of substrings with count of each character as k

Source: https://www.geeksforgeeks.org/number-substrings-count-character-k/
Given a string and an integer k, find number of substrings in which all the different characters occurs exactly k times.
Looking for a solution in O(n), using two pointers/sliding window approach. I'm able to find only longest substrings satisfying this criteria but not substrings within that long substring.
For ex: ababbaba, k = 2
My solution finds abab, ababba etc, but not bb within ababba.
Can someone help me with the logic?
If you could edit your question to include your solution code, I'd be happy to help you with that.
For now I'm sharing my solution code (in java) which runs in O(n2). I've added enough comments to make the code self explanatory. Nonetheless the logic for the solution is as follows:
As you correctly pointed out, the problem can be solved using sliding window approach (with variable window size). The solution below considers all possible sub-strings, using nested for loops for setting start and end indices. For each sub-string, we check if every element in the sub-string occurs exactly k times.
To avoid recalculating the count for every sub-string, we maintain the count in a map, and keep putting new elements in the map as we increment the end index (slide the window). This ensures that our solution runs in O(n2) and not O(n3).
To further improve efficiency, we only check the count of individual elements if the sub-string's size matches our requirement. e.g. for n unique elements (keys in the map), the size of required sub-string would be n*k. If the sub-string's size doesn't match this value, there's no need to check how many times the individual characters occur.
import java.util.*;
/**
* Java program to count the number of perfect substrings in a given string. A
* substring is considered perfect if all the elements within the substring
* occur exactly k number of times.
*
* #author Codextor
*/
public class PerfectSubstring {
public static void main(String[] args) {
String s = "aabbcc";
int k = 2;
System.out.println(perfectSubstring(s, k));
s = "aabccc";
k = 2;
System.out.println(perfectSubstring(s, k));
}
/**
* Returns the number of perfect substrings in the given string for the
* specified value of k
*
* #param s The string to check for perfect substrings
* #param k The number of times every element should occur within the substring
* #return int The number of perfect substrings
*/
public static int perfectSubstring(String s, int k) {
int finalCount = 0;
/*
* Set the initial starting index for the subarray as 0, and increment it with
* every iteration, till the last index of the string is reached.
*/
for (int start = 0; start < s.length(); start++) {
/*
* Use a HashMap to store the count of every character in the subarray. We'll
* start with an empty map everytime we update the starting index
*/
Map<Character, Integer> frequencyMap = new HashMap<>();
/*
* Set the initial ending index for the subarray equal to the starting index and
* increment it with every iteration, till the last index of the string is
* reached.
*/
for (int end = start; end < s.length(); end++) {
/*
* Get the count of the character at end index and increase it by 1. If the
* character is not present in the map, use 0 as the default count
*/
char c = s.charAt(end);
int count = frequencyMap.getOrDefault(c, 0);
frequencyMap.put(c, count + 1);
/*
* Check if the length of the subarray equals the desired length. The desired
* length is the number of unique characters we've seen so far (size of the map)
* multilied by k (the number of times each character should occur). If the
* length is as per requiremets, check if each element occurs exactly k times
*/
if (frequencyMap.size() * k == (end - start + 1)) {
if (check(frequencyMap, k)) {
finalCount++;
}
}
}
}
return finalCount;
}
/**
* Returns true if every value in the map is equal to k
*
* #param map The map whose values are to be checked
* #param k The required value for keys in the map
* #return true if every value in the map is equal to k
*/
public static boolean check(Map<Character, Integer> map, int k) {
/*
* Iterate through all the values (frequency of each character), comparing them
* with k
*/
for (Integer i : map.values()) {
if (i != k) {
return false;
}
}
return true;
}
}
For a given value k and a string s of length n with alphabet size D, we can solve the problem in O(n*D).
We need to find sub-strings with each character having exactly k-occurences
Minimum size of such sub-string = k (when only one character is there)
Maximum size of such sub-string = k*D (when all characters are there)
So we will check for all sub-strings of sizes in range [k, k*D]
from collections import defaultdict
ALPHABET_SIZE = 26
def check(count, k):
for v in count.values():
if v != k and v != 0:
return False
return True
def countSubstrings(s, k):
total = 0
for d in range(1, ALPHABET_SIZE + 1):
size = d * k
count = defaultdict(int)
l = r = 0
while r < len(s):
count[s[r]] += 1
# if window size exceed `size`, then fix left pointer and count
if r - l + 1 > size:
count[s[l]] -= 1
l += 1
# if window size is adequate then check and update count
if r - l + 1 == size:
total += check(count, k)
r += 1
return total
def main():
string1 = "aabbcc"
k1 = 2
print(countSubstrings(string1, k1)) # output: 6
string2 = "bacabcc"
k2 = 2
print(countSubstrings(string2, k2)) # output: 2
main()
I can't give you a O(n) solution but I can give you a O(k*n) solution (better than O(n^2) mentioned in the geeksforgeeks page).
The idea is that max no. elements are 26. So, we don't have to check all the substrings, we just have to check substrings with length<=26*k (26*k length is the case when all elements will occur k times. If length is more than that then at least one element will have to occur at least k+1 times). Also, we need to check only those substrings whose lengths are a factor of k.
So, check all 26*k*l possible substrings! (assuming k<<l). Thus, solution is O(k*n) but with a bit high constant (26).
There are few observation which will help optimize the solution
Notice that, you don't need to check every possible size substrings, you just need to check substrings of size k, 2k, 3k so on up to ALPHABET_SIZE * k (remember Pigeonhole principle)
You can pre-calculate frequency of alphabets till certain index from any end and later you can use it to find the frequency of alphabets between any two indexes in O(26)
C++ Implementation of your problem in O(n * ALPHABET_SIZE^2)
I have added comments and diagrams to help you out in understanding code quickly
diagram 1
diagram 2
#include <bits/stdc++.h>
#define ll long long
#define ALPHABET_SIZE 26
using namespace std;
int main()
{
ios_base::sync_with_stdio(false);
cin.tie(NULL);
cout.tie(NULL);
int n, k;
string s;
cin >> n >> k;
cin >> s;
ll cnt = 0;
/**
* It will be storing frequency of each alphabets
**/
vector<int> f(ALPHABET_SIZE, 0);
/**
* It will store alphabets frequency till that index
**/
vector<vector<int>> v;
v.push_back(f);
/**
* Scan array from left to right and calculate the frequency of each alphabets till that index
* Now push that frequency array in v
* This loop will run for n times
**/
for (int i = 1; i <= n; i++)
{
f[s[i - 1] - 'a']++;
v.push_back(f);
}
/**
* This loop will run for k times
**/
for (int i = 0; i < k; i++)
{
/**
* start is the lower bound (left end from where window will start sliding)
**/
int start = i;
/**
* end is the upper bound (right end till where window will be sliding)
**/
int end = (n / k) * k + i;
if (end > n)
{
end -= k;
}
/**
* This loop will run for n/k times
**/
for (int j = start; j <= end; j += k)
{
/**
* This is a ALPHABET_SIZE * k size window
* It will be sliding between start and end (inclusive)
* This loop will run for at most ALPHABET_SIZE times
**/
for (int d = j + k; d <= min(ALPHABET_SIZE * k + j, end); d += k)
{
/**
* A flag to check weather substring is valid or not
**/
bool flag = true;
/**
* Check if frequencies at two different indexes differ only by zero or k (element wise)
* Note that frequencies at two different index can't be same
* This loop will run for ALPHABET_SIZE times
**/
for (int idx = 0; idx < ALPHABET_SIZE; idx++)
{
if (abs(v[j][idx] - v[d][idx]) != k && abs(v[j][idx] - v[d][idx]) != 0)
{
flag = false;
}
}
/**
* Increase the total count if flag is true
**/
if (flag)
{
cnt++;
}
}
}
}
/**
* Print the total count
**/
cout << cnt;
return 0;
}
if you want solution in simple way and not worried about time complexity. Here is the solution.
public class PerfecSubstring {
public static void main(String[] args) {
String st = "aabbcc";
int k = 2;
System.out.println(perfect(st, k));
}
public static int perfect(String st, int k) {
int count = 0;
for (int i = 0; i < st.length(); i++) {
for (int j = st.length(); j > i; j--) {
String sub = st.substring(i, j);
if (sub.length() > k && check(sub, k)) {
System.out.println(sub);
count++;
}
}
}
return count;
}
public static boolean check(String st, int k) {
Map<Character, Integer> map = new HashMap<>();
for (int i = 0; i < st.length(); i++) {
Character c = st.charAt(i);
map.put(c, map.getOrDefault(c, 0) + 1);
}
return map.values().iterator().next() == k && new HashSet<>(map.values()).size() == 1;
}
}
Here is an answer I did in C#, with O(n^2) complexity. I probably should have used a helper method to avoid having a large chunk of code, but it does the job. :)
namespace CodingChallenges
{
using System;
using System.Collections.Generic;
class Solution
{
// Returns the number of perfect substrings of repeating character value 'num'.
public static int PerfectSubstring(string str, int num)
{
int count = 0;
for (int startOfSliceIndex = 0; startOfSliceIndex < str.Length - 1; startOfSliceIndex++)
{
for (int endofSliceIndex = startOfSliceIndex + 1; endofSliceIndex < str.Length; endofSliceIndex++)
{
Dictionary<char, int> dict = new Dictionary<char, int>();
string slice = str.Substring(startOfSliceIndex, (endofSliceIndex - startOfSliceIndex) + 1);
for (int i = 0; i < slice.Length; i++)
{
if (dict.ContainsKey(slice[i]))
{
dict[slice[i]]++;
}
else
{
dict[slice[i]] = 1;
}
}
bool isPerfect = true;
foreach (var entry in dict)
{
if (entry.Value != num)
{
isPerfect = false;
}
}
if (isPerfect)
{
Console.WriteLine(slice);
count++;
}
}
}
if (count == 1)
{
Console.WriteLine(count + " perfect substring.");
}
else
{
Console.WriteLine(count + " perfect substrings.");
}
return count;
}
public static void Main(string[] args)
{
string test = "1102021222";
PerfectSubstring(test, 2);
}
}
}
This solution works in O(n*D)
I think it can be upgraded to be O(n) by replacing the hash_map(frozenset(head_sum_mod_k.items())) with a map implementation that updates its hash rather than recalculating it -
this can be done because only one entry of head_sum_mod_k is changed per iteration.
from copy import deepcopy
def countKPerfectSequences(string:str, k):
print(f'Processing \'{string}\', k={k}')
# init running sum
head_sum = {char: 0 for char in string}
tail_sum = deepcopy(head_sum)
tail_position = 0
# to match both 0 & k sequence lengths, test for mod k == 0
head_sum_mod_k = deepcopy(head_sum)
occurrence_positions = {frozenset(head_sum_mod_k.items()): [0]}
# iterate over string
perfect_counter = 0
for i, val in enumerate(string):
head_sum[val] += 1
head_sum_mod_k[val] = head_sum[val] % k
while head_sum[val] - tail_sum[val] > k:
# update tail to avoid longer than k sequnces
tail_sum[string[tail_position]] += 1
tail_position += 1
# print(f'str[{tail_position}..{i}]=\'{string[tail_position:i+1]}\', head_sum_mod_k={head_sum_mod_k} occurrence_positions={occurrence_positions}')
# get matching sequences between head and tail
indices = list(filter(lambda i: i >= tail_position, occurrence_positions.get(frozenset(head_sum_mod_k.items()), [])))
# for start in indices:
# print(f'{string[start:i+1]}')
perfect_counter += len(indices)
# add head
indices.append(i+1)
occurrence_positions[frozenset(head_sum_mod_k.items())] = indices
return perfect_counter

Finding the ranking of a word (permutations) with duplicate letters

I'm posting this although much has already been posted about this question. I didn't want to post as an answer since it's not working. The answer to this post (Finding the rank of the Given string in list of all possible permutations with Duplicates) did not work for me.
So I tried this (which is a compilation of code I've plagiarized and my attempt to deal with repetitions). The non-repeating cases work fine. BOOKKEEPER generates 83863, not the desired 10743.
(The factorial function and letter counter array 'repeats' are working correctly. I didn't post to save space.)
while (pointer != length)
{
if (sortedWordChars[pointer] != wordArray[pointer])
{
// Swap the current character with the one after that
char temp = sortedWordChars[pointer];
sortedWordChars[pointer] = sortedWordChars[next];
sortedWordChars[next] = temp;
next++;
//For each position check how many characters left have duplicates,
//and use the logic that if you need to permute n things and if 'a' things
//are similar the number of permutations is n!/a!
int ct = repeats[(sortedWordChars[pointer]-64)];
// Increment the rank
if (ct>1) { //repeats?
System.out.println("repeating " + (sortedWordChars[pointer]-64));
//In case of repetition of any character use: (n-1)!/(times)!
//e.g. if there is 1 character which is repeating twice,
//x* (n-1)!/2!
int dividend = getFactorialIter(length - pointer - 1);
int divisor = getFactorialIter(ct);
int quo = dividend/divisor;
rank += quo;
} else {
rank += getFactorialIter(length - pointer - 1);
}
} else
{
pointer++;
next = pointer + 1;
}
}
Note: this answer is for 1-based rankings, as specified implicitly by example. Here's some Python that works at least for the two examples provided. The key fact is that suffixperms * ctr[y] // ctr[x] is the number of permutations whose first letter is y of the length-(i + 1) suffix of perm.
from collections import Counter
def rankperm(perm):
rank = 1
suffixperms = 1
ctr = Counter()
for i in range(len(perm)):
x = perm[((len(perm) - 1) - i)]
ctr[x] += 1
for y in ctr:
if (y < x):
rank += ((suffixperms * ctr[y]) // ctr[x])
suffixperms = ((suffixperms * (i + 1)) // ctr[x])
return rank
print(rankperm('QUESTION'))
print(rankperm('BOOKKEEPER'))
Java version:
public static long rankPerm(String perm) {
long rank = 1;
long suffixPermCount = 1;
java.util.Map<Character, Integer> charCounts =
new java.util.HashMap<Character, Integer>();
for (int i = perm.length() - 1; i > -1; i--) {
char x = perm.charAt(i);
int xCount = charCounts.containsKey(x) ? charCounts.get(x) + 1 : 1;
charCounts.put(x, xCount);
for (java.util.Map.Entry<Character, Integer> e : charCounts.entrySet()) {
if (e.getKey() < x) {
rank += suffixPermCount * e.getValue() / xCount;
}
}
suffixPermCount *= perm.length() - i;
suffixPermCount /= xCount;
}
return rank;
}
Unranking permutations:
from collections import Counter
def unrankperm(letters, rank):
ctr = Counter()
permcount = 1
for i in range(len(letters)):
x = letters[i]
ctr[x] += 1
permcount = (permcount * (i + 1)) // ctr[x]
# ctr is the histogram of letters
# permcount is the number of distinct perms of letters
perm = []
for i in range(len(letters)):
for x in sorted(ctr.keys()):
# suffixcount is the number of distinct perms that begin with x
suffixcount = permcount * ctr[x] // (len(letters) - i)
if rank <= suffixcount:
perm.append(x)
permcount = suffixcount
ctr[x] -= 1
if ctr[x] == 0:
del ctr[x]
break
rank -= suffixcount
return ''.join(perm)
If we use mathematics, the complexity will come down and will be able to find rank quicker. This will be particularly helpful for large strings.
(more details can be found here)
Suggest to programmatically define the approach shown here (screenshot attached below) given below)
I would say David post (the accepted answer) is super cool. However, I would like to improve it further for speed. The inner loop is trying to find inverse order pairs, and for each such inverse order, it tries to contribute to the increment of rank. If we use an ordered map structure (binary search tree or BST) in that place, we can simply do an inorder traversal from the first node (left-bottom) until it reaches the current character in the BST, rather than traversal for the whole map(BST). In C++, std::map is a perfect one for BST implementation. The following code reduces the necessary iterations in loop and removes the if check.
long long rankofword(string s)
{
long long rank = 1;
long long suffixPermCount = 1;
map<char, int> m;
int size = s.size();
for (int i = size - 1; i > -1; i--)
{
char x = s[i];
m[x]++;
for (auto it = m.begin(); it != m.find(x); it++)
rank += suffixPermCount * it->second / m[x];
suffixPermCount *= (size - i);
suffixPermCount /= m[x];
}
return rank;
}
#Dvaid Einstat, this was really helpful. It took me a WHILE to figure out what you were doing as I am still learning my first language(C#). I translated it into C# and figured that I'd give that solution as well since this listing helped me so much!
Thanks!
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Text.RegularExpressions;
namespace CsharpVersion
{
class Program
{
//Takes in the word and checks to make sure that the word
//is between 1 and 25 charaters inclusive and only
//letters are used
static string readWord(string prompt, int high)
{
Regex rgx = new Regex("^[a-zA-Z]+$");
string word;
string result;
do
{
Console.WriteLine(prompt);
word = Console.ReadLine();
} while (word == "" | word.Length > high | rgx.IsMatch(word) == false);
result = word.ToUpper();
return result;
}
//Creates a sorted dictionary containing distinct letters
//initialized with 0 frequency
static SortedDictionary<char,int> Counter(string word)
{
char[] wordArray = word.ToCharArray();
int len = word.Length;
SortedDictionary<char,int> count = new SortedDictionary<char,int>();
foreach(char c in word)
{
if(count.ContainsKey(c))
{
}
else
{
count.Add(c, 0);
}
}
return count;
}
//Creates a factorial function
static int Factorial(int n)
{
if (n <= 1)
{
return 1;
}
else
{
return n * Factorial(n - 1);
}
}
//Ranks the word input if there are no repeated charaters
//in the word
static Int64 rankWord(char[] wordArray)
{
int n = wordArray.Length;
Int64 rank = 1;
//loops through the array of letters
for (int i = 0; i < n-1; i++)
{
int x=0;
//loops all letters after i and compares them for factorial calculation
for (int j = i+1; j<n ; j++)
{
if (wordArray[i] > wordArray[j])
{
x++;
}
}
rank = rank + x * (Factorial(n - i - 1));
}
return rank;
}
//Ranks the word input if there are repeated charaters
//in the word
static Int64 rankPerm(String word)
{
Int64 rank = 1;
Int64 suffixPermCount = 1;
SortedDictionary<char, int> counter = Counter(word);
for (int i = word.Length - 1; i > -1; i--)
{
char x = Convert.ToChar(word.Substring(i,1));
int xCount;
if(counter[x] != 0)
{
xCount = counter[x] + 1;
}
else
{
xCount = 1;
}
counter[x] = xCount;
foreach (KeyValuePair<char,int> e in counter)
{
if (e.Key < x)
{
rank += suffixPermCount * e.Value / xCount;
}
}
suffixPermCount *= word.Length - i;
suffixPermCount /= xCount;
}
return rank;
}
static void Main(string[] args)
{
Console.WriteLine("Type Exit to end the program.");
string prompt = "Please enter a word using only letters:";
const int MAX_VALUE = 25;
Int64 rank = new Int64();
string theWord;
do
{
theWord = readWord(prompt, MAX_VALUE);
char[] wordLetters = theWord.ToCharArray();
Array.Sort(wordLetters);
bool duplicate = false;
for(int i = 0; i< theWord.Length - 1; i++)
{
if(wordLetters[i] < wordLetters[i+1])
{
duplicate = true;
}
}
if(duplicate)
{
SortedDictionary<char, int> counter = Counter(theWord);
rank = rankPerm(theWord);
Console.WriteLine("\n" + theWord + " = " + rank);
}
else
{
char[] letters = theWord.ToCharArray();
rank = rankWord(letters);
Console.WriteLine("\n" + theWord + " = " + rank);
}
} while (theWord != "EXIT");
Console.WriteLine("\nPress enter to escape..");
Console.Read();
}
}
}
If there are k distinct characters, the i^th character repeated n_i times, then the total number of permutations is given by
(n_1 + n_2 + ..+ n_k)!
------------------------------------------------
n_1! n_2! ... n_k!
which is the multinomial coefficient.
Now we can use this to compute the rank of a given permutation as follows:
Consider the first character(leftmost). say it was the r^th one in the sorted order of characters.
Now if you replace the first character by any of the 1,2,3,..,(r-1)^th character and consider all possible permutations, each of these permutations will precede the given permutation. The total number can be computed using the above formula.
Once you compute the number for the first character, fix the first character, and repeat the same with the second character and so on.
Here's the C++ implementation to your question
#include<iostream>
using namespace std;
int fact(int f) {
if (f == 0) return 1;
if (f <= 2) return f;
return (f * fact(f - 1));
}
int solve(string s,int n) {
int ans = 1;
int arr[26] = {0};
int len = n - 1;
for (int i = 0; i < n; i++) {
s[i] = toupper(s[i]);
arr[s[i] - 'A']++;
}
for(int i = 0; i < n; i++) {
int temp = 0;
int x = 1;
char c = s[i];
for(int j = 0; j < c - 'A'; j++) temp += arr[j];
for (int j = 0; j < 26; j++) x = (x * fact(arr[j]));
arr[c - 'A']--;
ans = ans + (temp * ((fact(len)) / x));
len--;
}
return ans;
}
int main() {
int i,n;
string s;
cin>>s;
n=s.size();
cout << solve(s,n);
return 0;
}
Java version of unrank for a String:
public static String unrankperm(String letters, int rank) {
Map<Character, Integer> charCounts = new java.util.HashMap<>();
int permcount = 1;
for(int i = 0; i < letters.length(); i++) {
char x = letters.charAt(i);
int xCount = charCounts.containsKey(x) ? charCounts.get(x) + 1 : 1;
charCounts.put(x, xCount);
permcount = (permcount * (i + 1)) / xCount;
}
// charCounts is the histogram of letters
// permcount is the number of distinct perms of letters
StringBuilder perm = new StringBuilder();
for(int i = 0; i < letters.length(); i++) {
List<Character> sorted = new ArrayList<>(charCounts.keySet());
Collections.sort(sorted);
for(Character x : sorted) {
// suffixcount is the number of distinct perms that begin with x
Integer frequency = charCounts.get(x);
int suffixcount = permcount * frequency / (letters.length() - i);
if (rank <= suffixcount) {
perm.append(x);
permcount = suffixcount;
if(frequency == 1) {
charCounts.remove(x);
} else {
charCounts.put(x, frequency - 1);
}
break;
}
rank -= suffixcount;
}
}
return perm.toString();
}
See also n-th-permutation-algorithm-for-use-in-brute-force-bin-packaging-parallelization.

Brute-force transposition decryption - word segmentation

I'm a 2nd year B. Comp. Sci. student and have a cryptography assignment that's really giving me grief. We've been given a text file of transposition-encrypted English phrases and an English dictionary file, then asked to write a program that deciphers the phrases automatically without any user input.
My first idea was to simply brute-force all possible permutations of the ciphertext, which should be trivial. However, I then have to decide which one is the most-likely to be the actual plaintext, and this is what I'm struggling with.
There's heaps of information on word segmentation here on SO, including this and this amongst other posts. Using this information and what I've already learned at uni, here's what I have so far:
string DecryptTransposition(const string& cipher, const string& dict)
{
vector<string> plain;
int sz = cipher.size();
int maxCols = ceil(sz / 2.0f);
int maxVotes = 0, key = 0;
// Iterate through all possible no.'s of cols.
for (int c = 2; c <= maxCols; c++)
{
int r = sz / c; // No. of complete rows if c is no. of cols.
int e = sz % c; // No. of extra letters if c is no. of cols.
string cipherCpy(cipher);
vector<string> table;
table.assign(r, string(c, ' '));
if (e > 0) table.push_back(string(e, ' '));
for (int y = 0; y < c; y++)
{
for (int x = 0; x <= r; x++)
{
if (x == r && e-- < 1) break;
table[x][y] = cipherCpy[0];
cipherCpy.erase(0, 1);
}
}
plain.push_back(accumulate(table.begin(),
table.end(), string("")));
// plain.back() now points to the plaintext
// generated from cipher with key = c
int votes = 0;
for (int i = 0, j = 2; (i + j) <= sz; )
{
string word = plain.back().substr(i, j);
if (dict.find('\n' + word + '\n') == string::npos) j++;
else
{
votes++;
i += j;
j = 2;
}
}
if (votes > maxVotes)
{
maxVotes = votes;
key = c;
}
}
return plain[key - 2]; // Minus 2 since we started from 2
}
There are two main problems with this algorithm:
It is incredibly slow, taking ~30 sec. to decrypt a 80-char. message.
It isn't completely accurate (I'd elaborate on this if I hadn't already taken up a whole page, but you can try it for yourself with the full VC++ 2012 project).
Any suggestions on how I could improve this algorithm would be greatly appreciated. MTIA :-)

Find the index of a specific combination without generating all ncr combinations

I am trying to find the index of a specific combination without generating the actual list of all possible combinations. For ex: 2 number combinations from 1 to 5 produces, 1,2;1,3,1,4,1,5;2,3,2,4,2,5..so..on. Each combination has its own index starting with zero,if my guess is right. I want to find that index without generating the all possible combination for a given combination. I am writing in C# but my code generates all possible combinations on fly. This would be expensive if n and r are like 80 and 9 and i even can't enumerate the actual range. Is there any possible way to find the index without producing the actual combination for that particular index
public int GetIndex(T[] combination)
{
int index = (from i in Enumerable.Range(0, 9)
where AreEquivalentArray(GetCombination(i), combination)
select i).SingleOrDefault();
return index;
}
I found the answer to my own question in simple terms. It is very simple but seems to be effective in my situation.The choose method is brought from other site though which generates the combinations count for n items chosen r:
public long GetIndex(T[] combinations)
{
long sum = Choose(items.Count(),atATime);
for (int i = 0; i < combinations.Count(); i++)
{
sum = sum - Choose(items.ToList().IndexOf(items.Max())+1 - (items.ToList().IndexOf(combinations[i])+1), atATime - i);
}
return sum-1;
}
private long Choose(int n, int k)
{
long result = 0;
int delta;
int max;
if (n < 0 || k < 0)
{
throw new ArgumentOutOfRangeException("Invalid negative parameter in Choose()");
}
if (n < k)
{
result = 0;
}
else if (n == k)
{
result = 1;
}
else
{
if (k < n - k)
{
delta = n - k;
max = k;
}
else
{
delta = k;
max = n - k;
}
result = delta + 1;
for (int i = 2; i <= max; i++)
{
checked
{
result = (result * (delta + i)) / i;
}
}
}
return result;
}

KMP prefix table

I am reading about KMP for string matching.
It needs a preprocessing of the pattern by building a prefix table.
For example for the string ababaca the prefix table is: P = [0, 0, 1, 2, 3, 0, 1]
But I am not clear on what does the numbers show. I read that it helps to find matches of the pattern when it shifts but I can not connect this info with the numbers in the table.
Every number belongs to corresponding prefix ("a", "ab", "aba", ...) and for each prefix it represents length of longest suffix of this string that matches prefix. We do not count whole string as suffix or prefix here, it is called self-suffix and self-prefix (at least in Russian, not sure about English terms).
So we have string "ababaca". Let's look at it. KMP computes Prefix Function for every non-empty prefix. Let's define s[i] as the string, p[i] as the Prefix function. prefix and suffix may overlap.
+---+----------+-------+------------------------+
| i | s[0:i] | p[i] | Matching Prefix/Suffix |
+---+----------+-------+------------------------+
| 0 | a | 0 | |
| 1 | ab | 0 | |
| 2 | aba | 1 | a |
| 3 | abab | 2 | ab |
| 4 | ababa | 3 | aba |
| 5 | ababac | 0 | |
| 6 | ababaca | 1 | a |
| | | | |
+---+----------+-------+------------------------+
Simple C++ code that computes Prefix function of string S:
vector<int> prefixFunction(string s) {
vector<int> p(s.size());
int j = 0;
for (int i = 1; i < (int)s.size(); i++) {
while (j > 0 && s[j] != s[i])
j = p[j-1];
if (s[j] == s[i])
j++;
p[i] = j;
}
return p;
}
This code may not be the shortest, but easy to understand flow of code.
Simple Java Code for calculating prefix-Array-
String pattern = "ababaca";
int i = 1, j = 0;
int[] prefixArray = new int[pattern.length];
while (i < pattern.length) {
while (pattern.charAt(i) != pattern.charAt(j) && j > 0) {
j = prefixArray[j - 1];
}
if (pattern.charAt(i) == pattern.charAt(j)) {
prefixArray[i] = j + 1;
i++;
j++;
} else {
prefixArray[i] = j;
i++;
}
}
for (int k = 0; k < prefixArray.length; ++k) {
System.out.println(prefixArray[k]);
}
It produces the required output-
0
0
1
2
3
0
1
Python Implementation
p='ababaca'
l1 = len(p)
j = 0
i = 1
prefix = [0]
while len(prefix) < l1:
if p[j] == p[i]:
prefix.append(j+1)
i += 1
j += 1
else:
if j == 0:
prefix.append(0)
i += 1
if j != 0:
j = prefix[j-1]
print prefix
I have tried my hands using the Javascript, Open for suggestions.
const prefixArray = function (p) {
let aux = Array(p.length).fill(0);
// For index 0 the matched index will always be 0, so we will we start from 1
let i = 1;
let m = 0; // mismatched index will be from 0th
// run the loop on pattern length
while ( i < p.length) {
// 3 Cases here
// First when we have a match of prefix and suffix of pattern
if(p.charAt(i) === p.charAt(m)) {
// increment m
m++;
// update aux index
aux[i] = m;
// update the index.
i++;
}
// Now if there is no match and m !=0 means some match happened previously
// then we need to move back M to that index
else if(p.charAt(i) !== p.charAt(m) && m !== 0) {
m = aux[m-1];
// we dont want to increment I as we want to start comparing this suffix with previous matched
} else {
// if none of the above conditions then
// just update the current index in aux array to 0
aux[i] = 0; // no match
i++; // shift to the next char
}
}
return aux;
}
No offset version
This is based on the idea of what I call todo indexing:
int confix[1000000];
void build_confix(char *pattern) {
// build len %
int len_pat = strlen(pattern);
// i, j using todo-indexing.
int j, i;
confix[j = 1] = i = 0;
while (j < strlen(pattern)) {
whlie (i && pattern[j] != pattern[i])
// length -> length mapping, no offset
i = confix[i];
confix[++j] = pattern[j] == pattern[i]?
++i:
0;
}
}
Then you can use this confix[] table to find needles in the middle(test)
int kmp_find_first(char *test, char *needle) {
int j = 0, i = 0;
while (j < strlen(test)) {
while (i && test[j] != needle[i])
i = confix[i];
++j; test[j] == needle[i]?
++i:
0;
if (i == strlen(needle))
return j - strlen(needle);
}
return -1;
}

Resources