This is an interview question. Count all numbers with unique digits (in decimal) in the range [1, N].
The obvious solution is to test each number in the range if its digits are unique. We can also generate all numbers with unique digits (as permutations) and test if they are in the range.
Now I wonder if there is a DP (dynamic programming) solution for this problem.
I'm thinking:
Number of unique digits numbers 1-5324
= Number of unique digits numbers 1-9
+ Number of unique digits numbers 10-99
+ Number of unique digits numbers 100-999
+ Number of unique digits numbers 1000-5324
So:
f(n) = Number of unique digits numbers with length n.
f(1) = 9 (1-9)
f(2) = 9*9 (1-9 * 0-9 (excluding first digit))
f(3) = 9*9*8 (1-9 * 0-9 (excluding first digit) * 0-9 (excluding first 2 digits))
f(4) = 9*9*8*7
Add all of the above until you get to the number of digits that N has minus 1.
Then you only have to do Number of unique digits numbers 1000-5324
And:
Number of unique digits numbers 1000-5324
= Number of unique digits numbers 1000-4999
+ Number of unique digits numbers 5000-5299
+ Number of unique digits numbers 5300-5319
+ Number of unique digits numbers 5320-5324
So:
N = 5324
If N[0] = 1, there are 9*8*7 possibilities for the other digits
If N[0] = 2, there are 9*8*7 possibilities for the other digits
If N[0] = 3, there are 9*8*7 possibilities for the other digits
If N[0] = 4, there are 9*8*7 possibilities for the other digits
If N[0] = 5
If N[1] = 0, there are 8*7 possibilities for the other digits
If N[1] = 1, there are 8*7 possibilities for the other digits
If N[1] = 2, there are 8*7 possibilities for the other digits
If N[1] = 3
If N[2] = 0, there are 7 possibilities for the other digits
If N[2] = 1, there are 7 possibilities for the other digits
If N[2] = 2
If N[3] = 0, there is 1 possibility (no other digits)
If N[3] = 1, there is 1 possibility (no other digits)
If N[3] = 2, there is 1 possibility (no other digits)
If N[3] = 3, there is 1 possibility (no other digits)
The above is something like:
uniques += (N[0]-1)*9!/(9-N.length+1)!
for (int i = 1:N.length)
uniques += N[i]*(9-i)!/(9-N.length+1)!
// don't forget N
if (hasUniqueDigits(N))
uniques += 1
You don't really need DP as the above should be fast enough.
EDIT:
The above actually needs to be a little more complicated (N[2] = 2 and N[3] = 2 above is not valid). It needs to be more like:
binary used[10]
uniques += (N[0]-1)*9!/(9-N.length+1)!
used[N[0]] = 1
for (int i = 1:N.length)
uniques += (N[i]-sum(used 0 to N[i]))*(9-i)!/(9-N.length+1)!
if (used[N[i]] == 1)
break
used[N[i]] = 1
// still need to remember N
if (hasUniqueDigits(N))
uniques += 1
For an interview question like this, a brute-force algorithm is probably intended, to demonstrate logic and programming competency. But also important is demonstrating knowledge of a good tool for the job.
Sure, after lots of time spent on the calculation, you can come up with a convoluted mathematical formula to shorten a looping algorithm. But this question is a straightforward example of pattern-matching, so why not use the pattern-matching tool built in to just about any language you'll be using: regular expressions?
Here's an extremely simple solution in C# as an example:
string csv = string.Join(",", Enumerable.Range(1, N));
int numUnique = N - Regex.Matches(csv, #"(\d)\d*\1").Count;
Line 1 will differ depending on the language you use, but it's just creating a CSV of all the integers from 1 to N.
But Line 2 would be very similar no matter what language: count how many times the pattern matches in the csv.
The regex pattern matches a digit possibly followed by some other digits, followed by a duplicate of the first digit.
Lazy man's DP:
Prelude> :m +Data.List
Data.List> length [a | a <- [1..5324], length (show a) == length (nub $ show a)]
2939
Although this question had been posted in 2013, I feel like it is still worthy to provide an implementation for reference as other than the algorithm given by Dukeling I couldn't find any implementation on the internet.
I wrote the code in Java for both brute force and Dukeling's permutation algorithm and, if I'm correct, they should always yield the same results.
Hope it can help somebody trying so hard to find an actual running solution.
public class Solution {
public static void main(String[] args) {
test(uniqueDigitsBruteForce(5324), uniqueDigits(5324));
test(uniqueDigitsBruteForce(5222), uniqueDigits(5222));
test(uniqueDigitsBruteForce(5565), uniqueDigits(5565));
}
/**
* A math version method to count numbers with distinct digits.
* #param n
* #return
*/
static int uniqueDigits(int n) {
int[] used = new int[10];
String seq = String.valueOf(n);
char[] ca = seq.toCharArray();
int uniq = 0;
for (int i = 1; i <= ca.length - 1; i++) {
uniq += uniqueDigitsOfLength(i);
}
uniq += (getInt(ca[0]) - 1) * factorial(9) / factorial(9 - ca.length + 1);
used[getInt(ca[0])] = 1;
for (int i = 1; i < ca.length; i++) {
int count = 0;
for (int j = 0; j < getInt(ca[i]); j++) {
if (used[j] != 1) count++;
}
uniq += count * factorial(9 - i) / factorial(9 - ca.length + 1);
if (used[getInt(ca[i])] == 1)
break;
used[getInt(ca[i])] = 1;
}
if (isUniqueDigits(n)) {
uniq += 1;
}
return uniq;
}
/**
* A brute force version method to count numbers with distinct digits.
* #param n
* #return
*/
static int uniqueDigitsBruteForce(int n) {
int count = 0;
for (int i = 1; i <= n; i++) {
if (isUniqueDigits(i)) {
count++;
}
}
return count;
}
/**
* http://oeis.org/A073531
* #param n
* #return
*/
static int uniqueDigitsOfLength(int n) {
if (n < 1) return 0;
int count = 9;
int numOptions = 9;
while(--n > 0) {
if (numOptions == 0) {
return 0;
}
count *= numOptions;
numOptions--;
}
return count;
}
/**
* Determine if given number consists of distinct digits
* #param n
* #return
*/
static boolean isUniqueDigits(int n) {
int[] used = new int[10];
if (n < 10) return true;
while (n > 0) {
int digit = n % 10;
if (used[digit] == 1)
return false;
used[digit] = 1;
n = n / 10;
}
return true;
}
static int getInt(char c) {
return c - '0';
}
/**
* Calculate Factorial
* #param n
* #return
*/
static int factorial(int n) {
if (n > 9) return -1;
if (n < 2) return 1;
int res = 1;
for (int i = 2; i <= n; i++) {
res *= i;
}
return res;
}
static void test(int expected, int actual) {
System.out.println("Expected Result: " + expected.toString());
System.out.println("Actual Result: " + actual.toString());
System.out.println(expected.equals(actual) ? "Correct" : "Wrong Answer");
}
}
a python solution is summarized as follow :
the solution is based on the mathematical principle of Bernhard Barker provided previous in the answer list:
thanks to Bernhard's ideal
def count_num_with_DupDigits(self, n: int) -> int:
# Fill in your code for the function. Do not change the function name
# The function should return an integer.
n_str = str(n)
n_len = len(n_str)
n_unique = 0
# get the all the x000 unique digits
if n > 10:
for i in range(n_len-1):
n_unique = n_unique + 9*int(np.math.factorial(9)/np.math.factorial(10-n_len+i+1))
m=0
if m == 0:
n_first = (int(n_str[m])-1)*int(np.math.factorial(9)/np.math.factorial(10-n_len))
m=m+1
count_last=0
n_sec=0
for k in range(n_len-1):
if m == n_len-1:
count_last = int(n_str[m])+1
for l in range(int(n_str[m])+1):a
if n_str[0:n_len-1].count(str(l)) > 0:
count_last = count_last-1
else:
for s in range(int(n_str[k+1])):
if n_str[0:k+1].count(str(s))>0:
n_sec=n_sec
else:
n_sec = int(np.math.factorial(9-m)/np.math.factorial(10-n_len))+n_sec
if n_str[0:k+1].count(n_str[k+1]) > 0:
break
m= m+1
value=n-(n_sec+n_first+n_unique+count_last)
else:
value = 0
return value
import java.io.*;
import java.util.*;
import java.text.*;
import java.math.*;
import java.util.regex.*;
public class Solution {
public static void main(String[] args) {
int rem;
Scanner in=new Scanner(System.in);
int num=in.nextInt();
int length = (int)(Math.log10(num)+1);//This one is to find the length of the number i.e number of digits of a number
int arr[]=new int[length]; //Array to store the individual numbers of a digit for example 123 then we will store 1,2,3 in the array
int count=0;
int i=0;
while(num>0) //Logic to store the digits in array
{ rem=num%10;
arr[i++]=rem;
num=num/10;
}
for( i=0;i<length;i++) //Logic to find the duplicate numbers
{
for(int j=i+1;j<length;j++)
{
if(arr[i]==arr[j])
{
count++;
break;
}
}
}
//Finally total number of digits minus duplicates gives the output
System.out.println(length-count);
}
}
Here is what you want, implemented by Python
def numDistinctDigitsAtMostN(n):
nums = [int(i) for i in str(n+1)]
k = len(str(n+1))
res = 0
# Here is a paper about Number of n-digit positive integers with all digits distinct
# http://oeis.org/A073531
# a(n) = 9*9!/(10-n)!
# calculate the number of n-digit positive integers with all digits distinct
for i in range(1, k):
res += 9 * math.perm(9,i-1)
# count no duplicates for numbers with k digits and smaller than n
for i, x in enumerate(nums):
if i == 0:
digit_range = range(1,x) # first digit can not be 0
else:
digit_range = range(x)
for y in digit_range:
if y not in nums[:i]:
res += math.perm(9-i,k-1-i)
if x in nums[:i]:
break
return res
And here are some good test cases.
They are big enough to test my code.
numDistinctDigitsAtMostN(100) = 90 #(9+81)
numDistinctDigitsAtMostN(5853) = 3181
numDistinctDigitsAtMostN(5853623) = 461730
numDistinctDigitsAtMostN(585362326) = 4104810
Related
From Codechef:
A string is considered balanced if and only if all the characters occur in it equal number of times.
You are given a string S; this string may only contain uppercase English letters. You may perform the following operation any number of times (including zero): Choose one letter in S and replace it by another uppercase English letter. Note that even if the replaced letter occurs in S multiple times, only the chosen occurrence of this letter is replaced.
Find the minimum number of operations required to convert the given string to a balanced string.
Example:
For input: ABCB
Here, we can replace C with A, TO GET: ABAB, where each character of the string occurs for 2 times.
So, the number of minimum operation(s)=1.
How to make the string good?
Can I apply dynamic programming to it ?
I don't think you really need dynamic programming here.
One approach in O(length(S)) time:
Iterate over S, build a frequency-map (a mapping from distinct letters A–Z to counts). For your ABCB example, that would be A->1 B->2 C->1 D->0 E->0 ... Z->0, which we can represent as the array [1, 2, 1, 0, 0, ..., 0].
We can do this because we don't actually care about the positions of letters at all; there's no real difference between ABCB and ABBC, in that each can be balanced by replacing its C with an A.
Sort the array. For your example, that gives [0, 0, ..., 0, 1, 1, 2].
We can do this because we don't actually care about which letter was which; there's no real difference between ABCB and ABDB, in that each can be balanced by replacing one of its singleton letters with its other one.
Assuming the string is nonempty (since if it's empty then the answer is just 0), the final balanced string will contain at least 1 and at most 26 distinct letters. For each integer i between 1 and 26, determine how many operations you'd need to perform in order to produce a balanced string with i distinct letters:
First, check that length(S) is a multiple of i; if not, this isn't possible, so skip to the next integer.
Next, compute length(S)/i, the count of each distinct letter in the final balanced string.
To count how many operations need to be performed, we're going to examine all the counts that need to be increased. (We could, equivalently, examine the counts that need to be decreased: they'll have to match.)
We're only interested in the last i elements of the sorted array. Any elements before that point represent letters that won't occur in the balanced string: either the counts are already zero and will remain so, or they are nonzero but will be decreased to zero. Either way, since we're only tracking increases, we can ignore them.
For each of the last i counts that are less than length(S)/i, add the difference. This sum is the number of operations. (Note that, since the counts are sorted, you can exit this inner loop as soon as you get to a count that's greater than or equal to the target count.)
You can exit this loop after the first i that's greater than or equal to the number of distinct letters in the original S (aside from values of i that we had to skip because they don't evenly divide length(S)). For example, if length(S) = 100, and the original S has 19 distinct letters, then we only need to consider i as high as 20. (Hat-tip to Eric Wang for suggesting something along these lines.)
Return the least of these up-to-26 sums. (Note that you don't actually need to store all the sums; you can just keep track of the minimum.)
Following code implement the solution, in Java, with unit test.
The algorithm is almost identical as in #ruakh's answer, if not identical.
Code
BalanceString.java
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
/**
* Assume string only contains A-Z, the 26 uppercase letter,
* <p>given a string, you can replace a char with another char from the 26 letter,
* <p>figure out the minimum replacement required to make the string balance,
* <p>which means each char in the string occurs the same time,
*
* #author eric
* #date 2/2/19 8:54 PM
*/
public class BalanceString {
private final char minChar;
private final char maxChar;
private final int distinctChars; // total distinct char count,
public static final BalanceString EN_UPPER_INSTANCE = new BalanceString('A', 'Z');
public BalanceString(char minChar, char maxChar) {
this.minChar = minChar;
this.maxChar = maxChar;
this.distinctChars = maxChar - minChar + 1;
if (distinctChars <= 0)
throw new IllegalArgumentException("invalid range of chars: [" + minChar + ", " + maxChar + "]");
}
/**
* Check minimal moves needed to make string balanced.
*
* #param str
* #return count of moves
*/
public int balanceCount(String str) {
// System.out.printf("string to balance:\t%s\n", str);
int len = str.length(); // get length,
if (len <= 2) return 0; // corner cases,
Map<Character, Integer> coMap = figureOccurs(str); // figure occurrences,
Integer[] occurs = sortOccursReversely(coMap); // reversely order occurrences,
int m = coMap.size(); // distinct char count,
int maxN = (len < distinctChars ? len : distinctChars); // get max possible distinct char count, included,
int smallestMoves = Integer.MAX_VALUE; // smallest moves, among all possible n,
// check each possible n, and get its moves,
for (int n = 1; n <= maxN; n++) {
if (len % n == 0) {
int moves = figureMoves(len, coMap, occurs, m, n);
if (moves < smallestMoves) smallestMoves = moves;
}
}
return smallestMoves;
}
/**
* Figure occurs for each char.
*
* #param str
* #return
*/
protected Map<Character, Integer> figureOccurs(String str) {
Map<Character, Integer> coMap = new HashMap<>();
for (char c : str.toCharArray()) {
if (c < minChar || c > maxChar)
throw new IllegalArgumentException(c + " is not within range 'A-Z'");
if (!coMap.containsKey(c)) coMap.put(c, 1);
else coMap.put(c, coMap.get(c) + 1);
}
return coMap;
}
/**
* Get reverse sorted occurrences.
*
* #param coMap
* #return
*/
protected Integer[] sortOccursReversely(Map<Character, Integer> coMap) {
Integer[] occurs = new Integer[coMap.size()];
coMap.values().toArray(occurs);
Arrays.sort(occurs, Collections.reverseOrder());
return occurs;
}
/**
* Figure moves needed to balance.
*
* #param len length of string,
* #param coMap
* #param m original distinct elements count,
* #param n new distinct elements count,
* #return count of moves,
*/
protected int figureMoves(int len, Map<Character, Integer> coMap, Integer[] occurs, int m, int n) {
int avgOccur = len / n; // average occurrence,
int moves = 0;
if (n == m) { // distinct chars don't change,
for (Integer occur : occurs) {
if (occur <= avgOccur) break;
moves += (occur - avgOccur);
}
} else if (n < m) { // distinct chars decrease,
for (int i = 0; i < n; i++) moves += Math.abs(occurs[i] - avgOccur); // for elements kept,
for (int i = n; i < m; i++) moves += occurs[i]; // for elements to replace,
moves >>= 1;
} else { // distinct chars increase,
for (int i = 0; i < occurs.length; i++) moves += Math.abs(occurs[i] - avgOccur); // for existing elements,
moves += ((n - m) * avgOccur); // for new elements,
moves >>= 1;
}
return moves;
}
public char getMinChar() {
return minChar;
}
public char getMaxChar() {
return maxChar;
}
public int getDistinctChars() {
return distinctChars;
}
}
BalanceStringTest.java
(Unit test, via TestNG)
import org.testng.Assert;
import org.testng.annotations.Test;
/**
* BalanceString test.
*
* #author eric
* #date 2/2/19 9:36 PM
*/
public class BalanceStringTest {
private BalanceString bs = BalanceString.EN_UPPER_INSTANCE;
#Test
public void test() {
// n < m case,
Assert.assertEquals(bs.balanceCount("AAAABBBC"), 1); // e.g 1A -> B,
Assert.assertEquals(bs.balanceCount("AAAAABBC"), 2); // e.g 1A -> B, 1C -> B,
Assert.assertEquals(bs.balanceCount("AAAAAABC"), 2); // e.g 1B -> A, 1C -> A,
Assert.assertEquals(bs.balanceCount("AAAAAAAB"), 1); // e.g 1B -> A,
// n > m case,
Assert.assertEquals(bs.balanceCount("AAAABBBBCCCCDDDDEEEEAAAA"), 4); // add 1 new char, e.g change 4 A to 4 F,
Assert.assertEquals(bs.balanceCount(genIncSeq(10)), 15); // A-J, 10 distinct chars, 55 in length; solved by add 1 new char, need 15 steps,
// n == m case,
Assert.assertEquals(bs.balanceCount(genIncSeq(3)), 1); // A-C, 3 distinct chars, 6 in length; symmetric, solved with same distinct chars, need 1 steps,
Assert.assertEquals(bs.balanceCount(genIncSeq(11)), 15); // A-K, 11 distinct chars, 66 in length; symmetric, solved with same distinct chars, need 15 steps,
// n < m, or n > m case,
Assert.assertEquals(bs.balanceCount("ABAC"), 1); // e.g 1A -> B, or 1A -> D,
}
// corner case,
#Test
public void testCorner() {
// m <= 2,
Assert.assertEquals(bs.balanceCount(""), 0);
Assert.assertEquals(bs.balanceCount("A"), 0);
Assert.assertEquals(bs.balanceCount("AB"), 0);
Assert.assertEquals(bs.balanceCount("AA"), 0);
/*------ m == n == distinctChars ------*/
String mndBalanced = genMndBalancedSeq(); // each possible char occurs exactly once, already balanced,
Assert.assertEquals(mndBalanced.length(), bs.getDistinctChars());
Assert.assertEquals(bs.balanceCount(mndBalanced), 0); // no need change,
char lastChar = mndBalanced.charAt(mndBalanced.length() - 1);
String mndOneDup = mndBalanced.replace(lastChar, (char) (lastChar - 1)); // (distinctChars -2) chars occur exactly once, one occurs twice, one is missing, thus it's one step away to balance,
Assert.assertEquals(mndOneDup.length(), bs.getDistinctChars());
Assert.assertEquals(bs.balanceCount(mndOneDup), 1); // just replace the duplicate char with missing char,
}
// invalid input,
#Test(expectedExceptions = IllegalArgumentException.class)
public void testInvalidInput() {
Assert.assertEquals(bs.balanceCount("ABAc"), 1);
}
// invalid char range, for constructor,
#Test(expectedExceptions = IllegalArgumentException.class)
public void testInvalidRange() {
new BalanceString('z', 'a');
}
/**
* Generate a string, with first char occur once, second twice, third three times, and so on.
* <p>e.g A, ABB, ABBCCC, ABBCCCDDDD,
*
* #param m distinct char count,
* #return
*/
private String genIncSeq(int m) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < m; i++) {
for (int j = 0; j <= i; j++) sb.append((char) (bs.getMinChar() + i));
}
return sb.toString();
}
/**
* Generate a string that contains each possible char exactly once.
* <p>For [A-Z], it could be: "ABCDEFGHIJKLMNOPQRSTUVWXYZ".
*
* #return
*/
private String genMndBalancedSeq() {
StringBuilder sb = new StringBuilder();
char minChar = bs.getMinChar();
int distinctChars = bs.getDistinctChars();
for (int i = 0; i < distinctChars; i++) {
sb.append((char) (minChar + i));
}
return sb.toString();
}
}
All test cases would pass.
Complexity
Time: O(len) + O(m * lg(m)) + O(m * factorCount)
Each sequential scan takes O(len), there are several sequential loop.
Sorting of array takes O(m*lg(m)), which is at most O(distinctChars * lg(distinctChars)), thus constant, and only sort once.
To figure out moves for each n, takes O(m).
The count of n that needs to figure moves, depends on count of divisible numbers for len, in the range [minChar, maxChar].
This count if kind small & constant too.
Space: O(len)
Input string need O(len).
Counter hashmap need O(m).
Sorted occurrence array need O(m).
Where:
len is string length.
m is distinct char count in original string
distinctChars is distinct char count, e.g 26.
maxN max possible distinct char count, included,
factorCount divisible number count in range [1, n], by len,
minChar min char, e.g A
maxChar max char, e.g Z
And:
len >= m
m <= distinctChars
if __name__ == "__main__":
for j in range(int(input())):
S = str(input())
N = len(S)
A = [0]*27
for c in S:
A[ord(c) - 65] = A[ord(c) - 65] + 1
A = sorted(A,reverse=True)
minSwap = N + 1
for i in range(1,27):
if N%i == 0:
temp = N//i
tempSwap = 0
for f in range(i):
if temp > A[f]:
tempSwap = tempSwap + temp - A[f]
if tempSwap <= minSwap:
minSwap = tempSwap
if minSwap == N+1:
minSwap = 0
print(minSwap)
Towards solving this problem ,I think it is also useful to find out all over extra presence( sum of number of elements which present more than one times) of different elements in string
for ex: in aabbc ,number of elements we have to remove to make presence of each element one is equal to 2 (this is called good string)
`x=input()
char=26
total=0
lis=[0]*char
#print(lis)
for i in range(len(x)):
lis[ord(x[i])-ord('a')]+=1
#print(lis)
for i in range(26):
if(lis[i]>1):
total=total+(lis[i]-1)
print(total)
`
#include <iostream>
#include <string>
#include <vector>
int countOps(std::vector<int> &map, int requiredFreq) {
int countOps = 0, greaterFreq = 0, lesserFreq = 0;
for (auto a : map) {
if (a > 0 && a < requiredFreq) {
lesserFreq = lesserFreq + abs(requiredFreq - a);
}
else if (a > 0 && a > requiredFreq) {
greaterFreq = greaterFreq + abs(requiredFreq - a);
}
}
countOps = greaterFreq > lesserFreq ? (lesserFreq + (greaterFreq - lesserFreq)) : lesserFreq;
return countOps;
}
int balanceString(std::string &s, long n){
std::vector<int> map(26, 0);
int distinctChar = 0;
int requiredFreq = -1;
int count = INT_MAX;
// Creating map with frequency and counting distinct chars
for (char x : s) {
if (map[x - 'a'] == 0) distinctChar++;
map[x - 'a']++;
}
std::sort(std::begin(map), std::end(map), std::greater<int>{});
// If size is multiple of distinctChar
if (n % distinctChar == 0) {
requiredFreq = int(n / distinctChar);
count = countOps(map, requiredFreq);
}
else {
for (int i = 1; i < distinctChar; i++) {
if (n % i == 0){
requiredFreq = int(n / i);
std::vector<int> temp(map.begin(), map.begin() + i);
int x = countOps(temp, requiredFreq);
count = std::min(count, x);
}
}
}
return count;
}
int main() {
std::string s = "aaaaccddefghiii";
long n = s.size();
if(n <= 1) return 0;
int count = balanceString(s, n);
std::cout << count << std::endl;
return 0;
}
Say we have
string a = "abc"
string b = "abcdcabaabccbaa"
Find location of all permutations of a in b. I am trying to find an effective algorithm for this.
Pseudo code:
sort string a // O(a loga)
for windows of length a in b // O(b)?
sort that window of b // O(~a loga)?
compare to a
if equal
save the index
So would this be a correct algorithm? Run time would be around O(aloga + ba loga) ~= O(a loga b)? How efficient would this be? Possibly way to reduce to O(a*b) or better?
sorting is very expensive, and doesn't use the fact you move along b with a sliding window.
I would use a comparison method that is location agnostic (since any permutation is valid) - assign each letter a prime number, and each string will be the multiplication of its letter values.
this way, as you go over b, each step requires just dividing by the letter you remove from he left, and multiplying with the next letter.
You also need to convince yourself that this indeed matches uniquely for each string and covers all permutations - this comes from the uniqueness of prime decomposition. Also note that on larger strings the numbers get big so you may need some library for large numbers
There is no need to hash, you can just count frequencies on your sliding window, and check if it matches. Assuming the size of your alphabet is s, you get a very simple O(s(n + m)) algorithm.
// a = [1 .. m] and b = [1 .. n] are the input
cnta = [1 .. s] array initialized to 0
cntb = [1 .. s] array initialized to 0
// nb_matches = the number of i s.t. cnta[i] = cntb[i]
// thus the current subword = a iff. nb_matches = s
nb_matches = s
for i = 1 to m:
if cntb[a[i]] = 0: nb_matches -= 1
cntb[a[i]] += 1
ans = 0
for i = 1 to n:
if cntb[b[i]] = cnta[b[i]]: nb_matches -= 1
cntb[b[i]] += 1
if nb_matches = s: ans += 1
if cntb[b[i]] = cnta[b[i]]: nb_matches += 1
if i - m + 1 >= 1:
if cntb[b[i - m + 1]] = cnta[b[i - m + 1]]: nb_matches -= 1
cntb[b[i - m + 1]] += 1
if cntb[b[i - m + 1]] = cnta[b[i - m + 1]]: nb_matches += 1
cntb[b[i - m + 1]] -= 1
return ans
Write a function strcount() to count the number of occurrences of character ch in a string or sub-sring str.
Then just pass through the search string.
for(i=0;i<haystacklenN-NeedleN+1;i++)
{
for(j=0;j<needleN;j++)
if(strcount(haystack + i, Nneedle, needle[j]) != strcount(needles, needlesN, needle[j])
break
}
if(j == needleN)
/* found a permuatation */
Below is my solution. The space complexity is just O(a + b), and the running time (if I can calculate correctly..) is O(b*a), as for each character in b, we may do a recursion a levels deep.
md5's answer is a good one and will be faster!!
public class FindPermutations {
public static void main(String[] args) {
System.out.println(numPerms(new String("xacxzaa"),
new String("fxaazxacaaxzoecazxaxaz")));
System.out.println(numPerms(new String("ABCD"),
new String("BACDGABCDA")));
System.out.println(numPerms(new String("AABA"),
new String("AAABABAA")));
// prints 4, then 3, then 3
}
public static int numPerms(final String a, final String b) {
int sum = 0;
for (int i = 0; i < b.length(); i++) {
if (permPresent(a, b.substring(i))) {
sum++;
}
}
return sum;
}
// is a permutation of a present at the start of b?
public static boolean permPresent(final String a, final String b) {
if (a.isEmpty()) {
return true;
}
if (b.isEmpty()) {
return false;
}
final char first = b.charAt(0);
if (a.contains(b.substring(0, 1))) {
// super ugly, but removes first from a
return permPresent(a.substring(0, a.indexOf(first)) + a.substring(a.indexOf(first)+1, a.length()),
b.substring(1));
}
return false;
}
}
For searchability's sake, I arrive on this page afer looking for other solutions to compare mine to, with the problem originating from watching this clip: https://www.hackerrank.com/domains/tutorials/cracking-the-coding-interview. The original problem statement was something like 'find all permutations of s in b'.
Use 2 hash tables and with a sliding window of size = length of smaller string:
int premutations_of_B_in_A(string large, string small) {
unordered_map<char, int> characters_in_large;
unordered_map<char, int> characters_in_small;
int ans = 0;
for (char c : small) {
characters_in_small[c]++;
}
for (int i = 0; i < small.length(); i++) {
characters_in_large[large[i]]++;
ans += (characters_in_small == characters_in_large);
}
for (int i = small.length(); i < large.length(); i++) {
characters_in_large[large[i]]++;
if (characters_in_large[large[i - small.length()]]-- == 1)
characters_in_large.erase(large[i - small.length()]);
ans += (characters_in_small == characters_in_large);
}
return ans;
}
This is almost solution but will help you to count occurrences of permutations of small strings into larger string
made for only lower case chars
This solution having --
Time Complexity - O(L)
where L is length of large input provided to problem, the exact would be to include 26 too for every char present in Large array but by ignoring constant terms, I will solely stand for this.
Space Complexity - O(1)
because 26 is also constant and independent of how large input would be.
int findAllPermutations(string small, string larger) {
int freqSmall[26] = {0};
//window size
int n = small.length();
//to return
int finalAns = 0;
for (char a : small) {
freqSmall[a - 97]++;
}
int freqlarger[26]={0};
int count = 0;
int j = 0;
for (int i = 0; larger[i] != '\0'; i++) {
freqlarger[larger[i] - 97]++;
count++;
if (count == n) {
count = 0;
int i;
for (i = 0; i < 26; i++) {
if (freqlarger[i] != freqSmall[i]) {
break;
}
}
if (i == 26) {
finalAns++;
}
freqlarger[larger[j] - 97]--;
j++;
}
}
return finalAns;
}
int main() {
string s, t;
cin >> s >> t;
cout << findAllPermutations(s, t) << endl;
return 0;
}
I'm working on a series of substring problem:
Given a string:
Find the substring containing only two unique characters that has maximum length.
Find the number of all substrings containing AT MOST two unique characters.
Find the number of all substrings containing two unique characters.
Seems like problem 1 and 2 has O(n) solution. However I cannot think of a O(n) solution for problem 3.(Here is the solution for problem 2 and here is for problem 1.).
So I would like to know does a O(n) solution for problem 3 exist or not?
Adding sample input/output for problem 3:
Given: abbac
Return: 6
Because there are 6 substring containing two unique chars:
ab,abb,abba,bba,ba,ac
Find the number of all substrings containing two unique characters.
Edit : I misread the question. This solution finds unique substrings with at least 2 unique characters
The number of substrings for a given word whose length is len is given by len * (len + 1) / 2
sum = len * (len + 1) / 2
We are looking for substrings whose length is greater than 1. The above formula includes substrings which are of length 1. We need to substract those substrings.
So the total number of 2 letter substrings now is len * (len + 1) / 2 - l.
sum = `len * (len + 1) / 2 - l`
Find the longest consecutive run of characters which are alike. Apply step 1 and 2.
Subtract this current sum from the sum as obtained from step 2.
Sample implementation follows.
public static int allUniq2Substrings(char s[]) {
int sum = s.length * (s.length + 1) / 2 - s.length;
int sameRun = 0;
for (int i = 0, prev = -1; i < s.length; prev = s[i++]) {
if (s[i] != prev) {
sum -= sameRun * (sameRun + 1) / 2 - sameRun;
sameRun = 1;
} else {
sameRun++;
}
}
return sum - (sameRun * (sameRun + 1) / 2 - sameRun);
}
allUniq2Substrings("aaac".toCharArray());
3
allUniq2Substrings("aabc".toCharArray());
5
allUniq2Substrings("aaa".toCharArray());
0
allUniq2Substrings("abcd".toCharArray());
6
Edit
Let me try this again. I use the above 3 invariants.
This is a subproblem of finding all substrings which contain at least 2 unique characters.
I have a method posted above which gives me unique substrings for any length. I will use it to generate substrings from a set which contains at 2 unique characters.
We only need to keep track of the longest consequent run of characters whose set length is 2. ie Any permutation of 2 unique characters. The sum of such runs gives us the total number of desired substrings.
public static int allUniq2Substrings(char s[]) {
int sum = s.length * (s.length + 1) / 2 - s.length;
int sameRun = 0;
for (int i = 0, prev = -1; i < s.length; prev = s[i++]) {
if (s[i] != prev) {
sum -= sameRun * (sameRun + 1) / 2 - sameRun;
sameRun = 1;
} else {
sameRun++;
}
}
return sum - (sameRun * (sameRun + 1) / 2 - sameRun);
}
public static int uniq2substring(char s[]) {
int last = 0, secondLast = 0;
int sum = 0;
for (int i = 1; i < s.length; i++) {
if (s[i] != s[i - 1]) {
last = i;
break;
}
}
boolean OneTwo = false;
int oneTwoIdx = -1; //alternating pattern
for (int i = last + 1; i < s.length; ++i) {
if (s[secondLast] != s[i] && s[last] != s[i]) { //detected more than 2 uniq chars
sum += allUniq2Substrings(Arrays.copyOfRange(s, secondLast, i));
secondLast = last;
last = i;
if (OneTwo) {
secondLast = oneTwoIdx;
}
OneTwo = false;
} else if (s[i] != last) { //alternating pattern detected a*b*a
OneTwo = true;
oneTwoIdx = i;
}
}
return sum + allUniq2Substrings(Arrays.copyOfRange(s, secondLast, s.length));
}
uniq2substring("abaac".toCharArray())
6
uniq2substring("aab".toCharArray())
2
uniq2substring("aabb".toCharArray())
4
uniq2substring("ab".toCharArray())
1
I think the link posted by you for the solution of the problem 2
http://coders-stop.blogspot.in/2012/09/directi-online-test-number-of.html
can we very easily be modelled for the solution of the third problem as well.
Just modify the driver program as under
int numberOfSubstrings ( string A ) {
int len = A.length();
int res = 0, j = 1, c = 1, a[2][2];
a[0][0] = A[0]; a[0][1] = 1;
for(int i=0;i<len;i++) {
>>int start = -1;
for (;j<len; j++) {
c = isInArray(a, c, A[j]);
>> if (c == 2 && start != - 1) start = j;
if(c == -1) break;
}
>>c = removeFromArray(a,A[i]);
res = (res + j - start);
}
return res;
}
The complete explanation on the derivation can be found in the link itself :)
I recently went through an interview and was asked this question. Let me explain the question properly:
Given a number M (N-digit integer) and K number of swap operations(a swap
operation can swap 2 digits), devise an algorithm to get the maximum
possible integer?
Examples:
M = 132 K = 1 output = 312
M = 132 K = 2 output = 321
M = 7899 k = 2 output = 9987
My solution ( algorithm in pseudo-code). I used a max-heap to get the maximum digit out of N-digits in each of the K-operations and then suitably swapping it.
for(int i = 0; i<K; i++)
{
int max_digit_currently = GetMaxFromHeap();
// The above function GetMaxFromHeap() pops out the maximum currently and deletes it from heap
int index_to_swap_with = GetRightMostOccurenceOfTheDigitObtainedAbove();
// This returns me the index of the digit obtained in the previous function
// .e.g If I have 436659 and K=2 given,
// then after K=1 I'll have 936654 and after K=2, I should have 966354 and not 963654.
// Now, the swap part comes. Here the gotcha is, say with the same above example, I have K=3.
// If I do GetMaxFromHeap() I'll get 6 when K=3, but I should not swap it,
// rather I should continue for next iteration and
// get GetMaxFromHeap() to give me 5 and then get 966534 from 966354.
if (Value_at_index_to_swap == max_digit_currently)
continue;
else
DoSwap();
}
Time complexity: O(K*( N + log_2(N) ))
// K-times [log_2(N) for popping out number from heap & N to get the rightmost index to swap with]
The above strategy fails in this example:
M = 8799 and K = 2
Following my strategy, I'll get M = 9798 after K=1 and M = 9978 after K=2. However, the maximum I can get is M = 9987 after K=2.
What did I miss?
Also suggest other ways to solve the problem & ways to optimize my solution.
I think the missing part is that, after you've performed the K swaps as in the algorithm described by the OP, you're left with some numbers that you can swap between themselves. For example, for the number 87949, after the initial algorithm we would get 99748. However, after that we can swap 7 and 8 "for free", i.e. not consuming any of the K swaps. This would mean "I'd rather not swap the 7 with the second 9 but with the first".
So, to get the max number, one would perform the algorithm described by the OP and remember the numbers which were moved to the right, and the positions to which they were moved. Then, sort these numbers in decreasing order and put them in the positions from left to right.
This is something like a separation of the algorithm in two phases - in the first one, you choose which numbers should go in the front to maximize the first K positions. Then you determine the order in which you would have swapped them with the numbers whose positions they took, so that the rest of the number is maximized as well.
Not all the details are clear, and I'm not 100% sure it handles all cases correctly, so if anyone can break it - go ahead.
This is a recursive function, which sorts the possible swap values for each (current-max) digit:
function swap2max(string, K) {
// the recursion end:
if (string.length==0 || K==0)
return string
m = getMaxDigit(string)
// an array of indices of the maxdigits to swap in the string
indices = []
// a counter for the length of that array, to determine how many chars
// from the front will be swapped
len = 0
// an array of digits to be swapped
front = []
// and the index of the last of those:
right = 0
// get those indices, in a loop with 2 conditions:
// * just run backwards through the string, until we meet the swapped range
// * no more swaps than left (K)
for (i=string.length; i-->right && len<K;)
if (m == string[i])
// omit digits that are already in the right place
while (right<=i && string[right] == m)
right++
// and when they need to be swapped
if (i>=right)
front.push(string[right++])
indices.push(i)
len++
// sort the digits to swap with
front.sort()
// and swap them
for (i=0; i<len; i++)
string.setCharAt(indices[i], front[i])
// the first len digits are the max ones
// the rest the result of calling the function on the rest of the string
return m.repeat(right) + swap2max(string.substr(right), K-len)
}
This is all pseudocode, but converts fairly easy to other languages. This solution is nonrecursive and operates in linear worst case and average case time.
You are provided with the following functions:
function k_swap(n, k1, k2):
temp = n[k1]
n[k1] = n[k2]
n[k2] = temp
int : operator[k]
// gets or sets the kth digit of an integer
property int : magnitude
// the number of digits in an integer
You could do something like the following:
int input = [some integer] // input value
int digitcounts[10] = {0, ...} // all zeroes
int digitpositions[10] = {0, ...) // all zeroes
bool filled[input.magnitude] = {false, ...) // all falses
for d = input[i = 0 => input.magnitude]:
digitcounts[d]++ // count number of occurrences of each digit
digitpositions[0] = 0;
for i = 1 => input.magnitude:
digitpositions[i] = digitpositions[i - 1] + digitcounts[i - 1] // output positions
for i = 0 => input.magnitude:
digit = input[i]
if filled[i] == true:
continue
k_swap(input, i, digitpositions[digit])
filled[digitpositions[digit]] = true
digitpositions[digit]++
I'll walk through it with the number input = 724886771
computed digitcounts:
{0, 1, 1, 0, 1, 0, 1, 3, 2, 0}
computed digitpositions:
{0, 0, 1, 2, 2, 3, 3, 4, 7, 9}
swap steps:
swap 0 with 0: 724886771, mark 0 visited
swap 1 with 4: 724876781, mark 4 visited
swap 2 with 5: 724778881, mark 5 visited
swap 3 with 3: 724778881, mark 3 visited
skip 4 (already visited)
skip 5 (already visited)
swap 6 with 2: 728776481, mark 2 visited
swap 7 with 1: 788776421, mark 1 visited
swap 8 with 6: 887776421, mark 6 visited
output number: 887776421
Edit:
This doesn't address the question correctly. If I have time later, I'll fix it but I don't right now.
How I would do it (in pseudo-c -- nothing fancy), assuming a fantasy integer array is passed where each element represents one decimal digit:
int[] sortToMaxInt(int[] M, int K) {
for (int i = 0; K > 0 && i < M.size() - 1; i++) {
if (swapDec(M, i)) K--;
}
return M;
}
bool swapDec(int[]& M, int i) {
/* no need to try and swap the value 9 as it is the
* highest possible value anyway. */
if (M[i] == 9) return false;
int max_dec = 0;
int max_idx = 0;
for (int j = i+1; j < M.size(); j++) {
if (M[j] >= max_dec) {
max_idx = j;
max_dec = M[j];
}
}
if (max_dec > M[i]) {
M.swapElements(i, max_idx);
return true;
}
return false;
}
From the top of my head so if anyone spots some fatal flaw please let me know.
Edit: based on the other answers posted here, I probably grossly misunderstood the problem. Anyone care to elaborate?
You start with max-number(M, N, 1, K).
max-number(M, N, pos, k)
{
if k == 0
return M
max-digit = 0
for i = pos to N
if M[i] > max-digit
max-digit = M[i]
if M[pos] == max-digit
return max-number(M, N, pos + 1, k)
for i = (pos + 1) to N
maxs.add(M)
if M[i] == max-digit
M2 = new M
swap(M2, i, pos)
maxs.add(max-number(M2, N, pos + 1, k - 1))
return maxs.max()
}
Here's my approach (It's not fool-proof, but covers the basic cases). First we'll need a function that extracts each DIGIT of an INT into a container:
std::shared_ptr<std::deque<int>> getDigitsOfInt(const int N)
{
int number(N);
std::shared_ptr<std::deque<int>> digitsQueue(new std::deque<int>());
while (number != 0)
{
digitsQueue->push_front(number % 10);
number /= 10;
}
return digitsQueue;
}
You obviously want to create the inverse of this, so convert such a container back to an INT:
const int getIntOfDigits(const std::shared_ptr<std::deque<int>>& digitsQueue)
{
int number(0);
for (std::deque<int>::size_type i = 0, iMAX = digitsQueue->size(); i < iMAX; ++i)
{
number = number * 10 + digitsQueue->at(i);
}
return number;
}
You also will need to find the MAX_DIGIT. It would be great to use std::max_element as it returns an iterator to the maximum element of a container, but if there are more you want the last of them. So let's implement our own max algorithm:
int getLastMaxDigitOfN(const std::shared_ptr<std::deque<int>>& digitsQueue, int startPosition)
{
assert(!digitsQueue->empty() && digitsQueue->size() > startPosition);
int maxDigitPosition(0);
int maxDigit(digitsQueue->at(startPosition));
for (std::deque<int>::size_type i = startPosition, iMAX = digitsQueue->size(); i < iMAX; ++i)
{
const int currentDigit(digitsQueue->at(i));
if (maxDigit <= currentDigit)
{
maxDigit = currentDigit;
maxDigitPosition = i;
}
}
return maxDigitPosition;
}
From here on its pretty straight what you have to do, put the right-most (last) MAX DIGITS to their places until you can swap:
const int solution(const int N, const int K)
{
std::shared_ptr<std::deque<int>> digitsOfN = getDigitsOfInt(N);
int pos(0);
int RemainingSwaps(K);
while (RemainingSwaps)
{
int lastHDPosition = getLastMaxDigitOfN(digitsOfN, pos);
if (lastHDPosition != pos)
{
std::swap<int>(digitsOfN->at(lastHDPosition), digitsOfN->at(pos));
++pos;
--RemainingSwaps;
}
}
return getIntOfDigits(digitsOfN);
}
There are unhandled corner-cases but I'll leave that up to you.
I assumed K = 2, but you can change the value!
Java code
public class Solution {
public static void main (String args[]) {
Solution d = new Solution();
System.out.println(d.solve(1234));
System.out.println(d.solve(9812));
System.out.println(d.solve(9876));
}
public int solve(int number) {
int[] array = intToArray(number);
int[] result = solve(array, array.length-1, 2);
return arrayToInt(result);
}
private int arrayToInt(int[] array) {
String s = "";
for (int i = array.length-1 ;i >= 0; i--) {
s = s + array[i]+"";
}
return Integer.parseInt(s);
}
private int[] intToArray(int number){
String s = number+"";
int[] result = new int[s.length()];
for(int i = 0 ;i < s.length() ;i++) {
result[s.length()-1-i] = Integer.parseInt(s.charAt(i)+"");
}
return result;
}
private int[] solve(int[] array, int endIndex, int num) {
if (endIndex == 0)
return array;
int size = num ;
int firstIndex = endIndex - size;
if (firstIndex < 0)
firstIndex = 0;
int biggest = findBiggestIndex(array, endIndex, firstIndex);
if (biggest!= endIndex) {
if (endIndex-biggest==num) {
while(num!=0) {
int temp = array[biggest];
array[biggest] = array[biggest+1];
array[biggest+1] = temp;
biggest++;
num--;
}
return array;
}else{
int n = endIndex-biggest;
for (int i = 0 ;i < n;i++) {
int temp = array[biggest];
array[biggest] = array[biggest+1];
array[biggest+1] = temp;
biggest++;
}
return solve(array, --biggest, firstIndex);
}
}else{
return solve(array, --endIndex, num);
}
}
private int findBiggestIndex(int[] array, int endIndex, int firstIndex) {
int result = firstIndex;
int max = array[firstIndex];
for (int i = firstIndex; i <= endIndex; i++){
if (array[i] > max){
max = array[i];
result = i;
}
}
return result;
}
}
I am given an input, "N", i have to find the number of list of length N, which starts with 1, such that the next number to be added is at most 1 more than the max number added till now. For Example,
N = 3, possible lists => (111, 112, 121, 122, 123), [113, or 131 is not possible as while adding '3' to the list, the maximum number present in the list would be '1', thus we can add only 1 or 2].
N = 4, the list 1213 is possible as while adding 3, the maximum number in the list is '2', thus 3 can be added.
Problem is to count the number of such lists possible for a given input "N".
My code is :-
public static void Main(string[] args)
{
var noOfTestCases = Convert.ToInt32(Console.ReadLine());
var listOfOutput = new List<long>();
for (int i = 0; i < noOfTestCases; i++)
{
var requiredSize = Convert.ToInt64(Console.ReadLine());
long result;
const long listCount = 1;
const long listMaxTillNow = 1;
if (requiredSize < 3)
result = requiredSize;
else
{
SeqCount.Add(requiredSize, 0);
AddElementToList(requiredSize, listCount, listMaxTillNow);
result = SeqCount[requiredSize];
}
listOfOutput.Add(result);
}
foreach (var i in listOfOutput)
{
Console.WriteLine(i);
}
}
private static Dictionary<long, long> SeqCount = new Dictionary<long, long>();
private static void AddElementToList(long requiredSize, long listCount, long listMaxTillNow)
{
if (listCount == requiredSize)
{
SeqCount[requiredSize] = SeqCount[requiredSize] + 1;
return;
}
var listMaxTillNowNew = listMaxTillNow + 1;
for(var i = listMaxTillNowNew; i > 0; i--)
{
AddElementToList(requiredSize, listCount + 1,
i == listMaxTillNowNew ? listMaxTillNowNew : listMaxTillNow);
}
return;
}
Which is the brute force method. I wish to know what might be the best algorithm for the problem?
PS : I only wish to know the number of such lists, so i am sure creating all the list won't be required. (The way i am doing in the code)
I am not at all good in algorithms, so please excuse for the long question.
This problem is a classic example of a dynamic programming problem:
If you define a function dp(k, m) to be the number of lists of length k for which the maximum number is m, then you have a recurrence relation:
dp(1, 1) = 1
dp(1, m) = 0, for m > 1
dp(k, m) = dp(k-1, m) * m + dp(k-1, m-1)
Indeed, there is only one list of length 1 and its maximum element is 1.
When you are building a list of length k with max element m, you can take any of the (k-1)-lists with max = m and append 1 or 2 or .... or m. Or you can take a (k-1)-list with max element m-1 and append m. If you take a (k-1)-list with max element less than m-1 then by your rule you can't get a max of m by appending just one element.
You can compute dp(k,m) for all k = 1,...,N and m = 1,...,N+1 using dynamic programming in O(N^2) and then the answer to your question would be
dp(N,1) + dp(N,2) + ... + dp(N,N+1)
Thus the algorithm is O(N^2).
See below for the implementation of dp calculation in C#:
int[] arr = new int[N + 2];
for (int m = 1; m < N + 2; m++)
arr[m] = 0;
arr[1] = 1;
int[] newArr = new int[N + 2];
int[] tmp;
for (int k = 1; k < N; k++)
{
for (int m = 1; m < N + 2; m++)
newArr[m] = arr[m] * m + arr[m - 1];
tmp = arr;
arr = newArr;
newArr = tmp;
}
int answer = 0;strong text
for (int m = 1; m < N + 2; m++)
answer += arr[m];
Console.WriteLine("The answer for " + N + " is " + answer);
Well, I got interrupted by a fire this afternoon (really!) but FWIW, here's my contribution:
/*
* Counts the number of possible integer list on langth N, with the
* property that no integer in a list(starting with one) may be more
* than one greater than the greatest integer preceeding it in the list.
*
* I am calling this "Semi-Factorial" since it is somewhat similar to
* the factorial function and its constituent integer combinations.
*/
public int SemiFactorial(int N)
{
int sumCounts = 0;
// get a list of the counts of all valid lists of length N,
//whose maximum integer is listCounts[maxInt].
List<int> listCounts = SemiFactorialCounts(N);
for (int maxInt = 1; maxInt <= N; maxInt++)
{
// Get the number of lists, of length N-1 whose maximum integer
//is (maxInt):
int maxIntCnt = listCounts[maxInt];
// just sum them up
sumCounts += maxIntCnt;
}
return sumCounts;
}
// Returns a list of the counts of all valid lists of length N, and
//whose maximum integer is [i], where [i] is also its index in this
//returned list. (0 is not used).
public List<int> SemiFactorialCounts(int N)
{
List<int> cnts;
if (N == 0)
{
// no valid lists,
cnts = new List<int>();
// (zero isn't used)
cnts.Add(0);
}
else if (N == 1)
{
// the only valid list is {1},
cnts = new List<int>();
// (zero isn't used)
cnts.Add(0);
//so that's one list of length 1
cnts.Add(1);
}
else
{
// start with the maxInt counts of lists whose length is N-1:
cnts = SemiFactorialCounts(N - 1);
// add an entry for (N)
cnts.Add(0);
// (reverse order because we overwrite the list using values
// from the next lower index.)
for (int K = N; K > 0; K--)
{
// The number of lists of length N and maxInt K { SF(N,K) }
// Equals K times # of lists one shorter, but same maxInt,
// Plus, the number of lists one shorter with maxInt-1.
cnts[K] = K * cnts[K] + cnts[K - 1];
}
}
return cnts;
}
pretty similar to the others. Though I wouldn't call this "classic dynamic programming" so much as just "classic recursion".