recursive binarySearch with array of strings - c++

My function works for the first 4 elements, but returns -1 for 12 and 14..even though there in the array.
I've tried changing some of the if statements and using .compare but haven't had any luck.
Any ideas?
Thanks
/** Searches the array anArray[first] through anArray[last]
for a given value by using a binary search.
#pre 0 <= first, last <= SIZE - 1, where SIZE is the
maximum size of the array, and anArray[first] <=
anArray[first + 1] <= ... <= anArray[last].
#post anArray is unchanged and either anArray[index] contains
the given value or index == -1.
#param anArray The array to search.
#param first The low index to start searching from.
#param last The high index to stop searching at.
#param target The search key.
#return Either index, such that anArray[index] == target, or -1.
*/
int binarySearch(const string anArray[], int first, int last, string target)
{
int index;
if (first > last)
index = -1; // target not in original array
else {
// If target is in anArray,
// anArray[first] <= target <= anArray[last]
int mid = first + (last - first) / 2;
if (anArray[mid] == target)
index = mid; // target found at anArray[mid]
else if (anArray[mid] > target)
// Point X
index = binarySearch(anArray, first, mid - 1, target);
else
// Point Y
index = binarySearch(anArray, mid + 1, last, target);
} // end if
return index;
}
int main()
{
const string v[6] = {"1","5","6","9","12","14"};
int index;
string searchItem = "12";
cout << "Searching For: " << searchItem << endl;
index = binarySearch(v, 0, 6, searchItem);
cout << "Element found at index " << index << endl;
system("pause");
return 0;
}

Problem is the fact that for binary search to work your array has to be sorted with the same criteria you use with your binary search. Array you provided is not sorted. You sorted numbers, but you have strings and they compare differently - lexicographically. Which means that "12" comes before "5" and so on. So you either need to convert strings to numbers before you compare, use proper data type (probably int) or change order of elements in your array accordingly.
Note: your function "works" for the first four elements because for single digit numbers lexicographical and numerical compare is the same.
Note2: you also seem to pass wrong number as last, your doc clearly says "SIZE - 1", which is equal to 5. (your array has indexes from 0 to 5, not to 6)

Problem is that you can't compare full strings like you compare numbers (although you can do that with chars according to ascii value). You have two options, the first and simpler one is defining a function to compare strings, something like:
int compareStrings (string a, string b) { // -1: a>b, 0: a=b, 1: a<b
if (a.length() == b.length()) {
if (a == b) return 0;
else for (int i = 0; i < a.length(); i++)
if (a[i] < b[i]) return 1;
else if (a[i] > b[i]) return -1;
}
else return (a.length() < b.length()? 1 : -1);
}
and then when you need to retrieve what string is bigger you could do:
int compResult = compareStrings (anArray[mid], target);
switch (compResult) {
case -1:
index = binarySearch(anArray, first, mid - 1, target); break;
case 1:
index = binarySearch(anArray, mid + 1, last, target); break;
}
The other one is creating a custom "wrapper" class for the strings such that you can override a comparing operator within it, but is a little bit more complicated, and the body of the functions would be pretty much the same, so I would suggest the former solution. But if you want to see how you could accomplish the latter of the solutions, here's a source:
https://www.geeksforgeeks.org/c-program-to-compare-two-strings-using-operator-overloading/
Hope it helps man

Related

Determining If an Array Can be Partitioned into Two Subsequences, Each with Increasing Order

I am currently struggling with a homework problem for my Algorithms Class. A summary of the instruction:
The user enters an integer 'n' to determine the number of test cases.
The user individually enters another integer 'num' to determine the # of elements in each test case.
The user enters the elements of the individual array.
The algorithm has to process the array and determine whether it can be partitioned into two subsequences, each of which is in strictly increasing order. If the result is positive, the program prints "Yes", otherwise it prints "No".
I have 24 hours to complete this assignment but am struggling with the primary problem - I cannot properly process the user input. (come up with an algorithm to split the two subsequences)
update: I got to this solution. It passes 4/5 tests but fails the time constraint in the last test.
#include<iostream>
#include<string>
using namespace std;
bool run(){
int numbers;
int *arr;
cin >> numbers;
arr = new int[numbers];
for (int i = 0; i < numbers; i++)
cin >> arr[i];
long long int MAX = 0;
long long int MAX2 = 0;
string stra = "";
string strb = "";
string result = "";
string total = "";
long long int sum = 0;
for (int i = 0; i < numbers; i++){
if (arr[i] >= MAX && arr[i] != arr[i - 1]){
stra += to_string(arr[i]);
MAX = arr[i];
}
else
if (arr[i] >= MAX2 && MAX2 != MAX){
strb += to_string(arr[i]);
MAX2 = arr[i];
}
}
for (int i = 0; i < numbers; i++){
result = to_string(arr[i]);
total += result;
}
long long int len1 = stra.length();
long long int len2 = strb.length();
sum += len1 + len2;
delete[] arr;
if (sum != total.length())
return false;
else
return true;
}
int main()
{
int test;
cin >> test;
while (test > 0)
{
if (run())
cout << "Yes\n";
else
cout << "No\n";
test--;
}
system("pause");
}
Example input:
2
5
3 1 5 2 4
5
4 8 1 5 3
Example output:
Yes
No
Explanation: For the array 3 1 5 2 4, the two strictly increasing subsequences are: 3 5 and 1 2 4.
It seems that the existence of any equal or decreasing subsequence of at least three elements means the array cannot be partitioned into two subsequences, each with strictly increasing order, since once we've placed the first element in one part and the second element in the other part, we have no place to place the third.
This seems to indicate that finding the longest decreasing or equal subsequence is a sure solution. Since we only need one of length 3, we can record in O(n) for each element if it has a greater or equal element to the left. Then perform the reverse. If any element has both a greater or equal partner on the left and a smaller or equal partner on the right, the answer is "no."
We can visualise the O(n) time, O(1) space method by plotting along value and position:
A choosing list B here
A x would be wrong
x
value B z
^ B x
| x
| A
| x
|
| B
| x
- - - - - - - -> position
We notice that as soon as a second list is established (with the first decrease), any element higher than the absolute max so far must be assigned to the list that contains it, and any element lower than it can, in any case, only be placed in the second list if at all.
If we were to assign an element higher than the absolute max so far to the second list (that does not contain it), we could arbitrarily construct a false negative by making the next element lower than both the element we just inserted into the second list and the previous absolute max, but greater than the previous max of the second list (z in the diagram). If we had correctly inserted the element higher than the previous absolute max into that first list, we'd still have room to insert the new, arbitrary element into the second list.
(The JavaScript code below technically uses O(n) space in order to show the partition but notice that we only rely on the last element of each part.)
function f(A){
let partA = [A[0]];
let partB = [];
for (let i=1; i<A.length; i++){
if (A[i] > partA[partA.length-1])
partA.push(A[i]);
else if (partB.length && A[i] <= partB[partB.length-1])
return false;
else
partB.push(A[i]);
}
return [partA, partB];
}
let str = '';
let examples = [
[30, 10, 50, 25, 26],
[3, 1, 5, 2, 4],
[4, 8, 1, 5, 3],
[3, 1, 1, 2, 4],
[3, 4, 5, 1, 2],
[3, 4, 1],
[4, 1, 2, 7, 3]
];
for (e of examples)
str += JSON.stringify(e) + '\n' + JSON.stringify(f(e)) + '\n\n';
console.log(str);
I would go over the entire array once and check two maximal values. If the actual array value is smaller than both maxima, it is not possible, otherwise the proper maximum is increased.
The algorithm does not have to traverse the whole array, if the split condition is violated before.
Here is my code
#include <algorithm>
#include <iostream>
#include <vector>
bool isAddable(const int item, int &max1, int &max2) {
if (max2 > item) {
return false;
}
else {
if (max1 > item) {
max2 = item;
}
else {
max1 = item;
}
return true;
}
}
void setStartValue(int &max1, int &max2, const std::vector<int> &vec) {
max1 = *std::min_element(vec.begin(), vec.begin() + 3);
max2 = *std::max_element(vec.begin(), vec.begin() + 3);
}
bool isDiviableIntoTwoIncreasingArrays(const std::vector<int> &vec) {
if (vec.size() < 3) {
return true;
}
int max1, max2;
setStartValue(max1, max2, vec);
for (int i = 2; i < vec.size(); ++i) {
if (max1 > max2) {
if (!isAddable(vec[i], max1, max2)) {
return false;
}
}
else {
if (!isAddable(vec[i], max2, max1)) {
return false;
}
}
}
return true;
}
int main() {
std::vector<int> userVec;
int tmp1;
while (std::cin >> tmp1) {
userVec.emplace_back(tmp1);
}
const std::vector<int> v1{3, 1, 5, 2, 4};
const std::vector<int> v2{4, 8, 1, 5, 3};
const std::vector<int> v3{3, 4, 1};
for (const std::vector<int> &vec : {userVec, v1, v2, v3}) {
if (isDiviableIntoTwoIncreasingArrays(vec)) {
std::cout << "Yes\n";
}
else {
std::cout << "No\n";
}
}
}
I think you could resort to using a brute force solution. Notice here I use vectors(I think you should as well) to store the data and I use recursion to exhaust out all possible combinations. Keep the problem in mind, solve it and then focus on trivial tasks like parsing the input and matching the way your coursework expects you to enter data. I have added inline comments to make this understandable.
bool canPartition(vector<int>& nums) {
if(nums.empty()) return false;
vector<int> part1 = {}, part2 = {}; // two partitions
auto ans = canPart(nums, part1, part2, 0); // pass this to our recursive function
return ans;
}
bool canPart(vector<int>& nums, vector<int>& part1, vector<int>& part2, int i)
{
if(i >= nums.size()) // we are at the end of the array is this a solution?
{
if(!part1.empty() && !part2.empty()) // only if the partitions are not empty
{
//if you want you could print part1 and part2 here
//to see what the partition looks like
return true;
}
return false;
}
bool resp1empty = false, resp2empty = false, resp1 = false, resp2 = false;
if(part1.empty()) // first partition is empty? lets add something
{
part1.push_back(nums[i]);
resp1empty = canPart(nums, part1, part2, i + 1);
part1.pop_back(); // well we need to remove this element and try another one
}
else if(nums[i] > part1.back()) // first partition is not empty lets check if the sequence is increasing
{
part1.push_back(nums[i]);
resp1 = canPart(nums, part1, part2, i + 1);
part1.pop_back();
}
if(part2.empty()) // is partition two empty? lets add something
{
part2.push_back(nums[i]);
resp2empty = canPart(nums, part1, part2, i + 1);
part2.pop_back();
}
else if(nums[i] > part2.back()) // check if sequence is increasing
{
part2.push_back(nums[i]);
resp2 = canPart(nums, part1, part2, i + 1);
part2.pop_back();
}
//if any of the recursive paths returns a true we have an answer
return resp1empty || resp2empty || resp1 || resp2;
}
You can now try this out with a main function:
vector<int> v = {3,1,5,2,4};
cout << canPartition(v);
The key take away is make a small test case, add a few more non trivial test cases, solve the problem and then look into parsing inputs for other test cases
I think this comes down to whether you have an option for a number to appear in the first list or second list or not.
So, we will keep adding numbers to list 1 and if we can't add any element, we will make it as the start of the new list.
Let's say, we have both the lists going. If we come across an element to whom we can't add to any of the lists, we return false.
There does arise a situation where we could add an element to any of the 2 lists. In this scenario, we adopt a greedy approach as to add to which list.
We prepare an array of minimum values from the right. For example, for [30,10,50,25,26], we will have an array of minimums as [10,25,25,26,(empty here since last)].
Now, let's trace how we could divide them into 2 lists properly.
30 => List A.
10 => List B. (since you can't add it first list, so make a new one from here)
50 => List A.
Here, 50 applies to come after either 30 or 10. If we choose 10, then we won't be able to accommodate the next 25 in either of the 2 lists and our program would fail here itself, since our lists would look like [30] and [10,50]. However, we could continue further if we add 50 to 30 by checking for the minimum stored for it in our minimums array, which is 25.
25 => List B.
26 => List B.
So, our final lists are [30,50] and [10,25,26].
Time complexity: O(n), Space complexity: O(n) and you can print the 2 lists as well.
If we come across a sorted array which is strictly increasing, we return true for them anyway.

Subset Sum using recursive

My Input is:
W[10] = {1, 3, 5, 7, 9, 12, 19, 22, 36, 63}
X[10] = {0};
M = 79;
I called the function by:
findSolution(0,0,177); <br>
Note: 177 is sum of all the elements inside W array.
void findSolution(int s, int k, int r) {
cout << "fn(" << s << " , " << k << ", " << r << " )" << endl;
X[k] = 1;
if (s + W[k] == M){
printArr(X);
}
else if (s + W[k] + W[k + 1] <= M) {
return findSolution(s + W[k], k + 1, r - W[k]);
}
if ((s + r - W[k] >= M) && (s + W[k + 1]) <= M){
X[k] = 0;
return findSolution(s, k + 1, r - W[k]);
}
}
Output:
fn(0 , 0, 177 )
fn(1 , 1, 176 )
fn(4 , 2, 173 )
fn(9 , 3, 168 )
fn(16 , 4, 161 )
fn(25 , 5, 152 )
fn(37 , 6, 140 )
fn(56 , 7, 121 )
The output given above is to track the function calls. The output ends here and doesn't go forward. What is wrong with my code. I am trying to print a subset which gives a desired sum = 79. The recursive call doesn't return back.
The problem with your solution is that it uses a greedy strategy (i.e. it does not "backtrack" after finding a suitable candidate).
Your algorithm checks for three conditions:
You found a solution,
A solution is possible if you add k-th element to the subset, or
A solution is possible if you replace k-1-st element with k-th.
This strategy does not exhaust all possibilities: for instance, it may not be possible to replace k-th element with k+1-st, but it may be possible to replace several elements ahead of k-th with k+1-st and obtain a solution. Your strategy is greedy, because when it discovers that an element could be added to a set (i.e. s + W[k] + W[k + 1] <= M) it takes that path, and never looks back (i.e. returns from that branch).
You can fix this by restructuring your code as follows:
Make your function return true when a solution is found, and false otherwise.
Keep your base case if (s + W[k] == M), and add a return true when a solution is found.
Check if it is possible to add k-th element to the set. If it is possible, add it, and try for the partial sum of s + W[k]
Check the return of the recursive invocation. If it is true, return true.
Otherwise, remove k-th element from the set, and make a second recursive invocation without the k-th element in the mix. Use the same partial sum of s.
Return the value of the last recursive invocation to the caller.
Now your algorithm is exhaustive, because for each element the algorithm tries to find a partial sum both when the element is part of the solution, and when the element is not part of the solution (i.e. O(2n) checks in all).
The recursive call is returning back; it is just doing so before you found a solution. This can happen if the last if is reached but fails. (Note that what looks like your base case, when you call printArr, does not necessarily stop the recursion.)
//code in c++ for return subset sum to k using recursion
int subsetSumToK(int input[], int n, int output[][50], int k) {
//as we are decreasing the value of k in int count1 recursive call, at a time value of k will be zero ,positive value and negative value also, so we will return only those subsets where the value of k is zero when the size of input array will become zero else we just return 0. In recursive calls we use two recursive calls, in first we are including the element, so as we including the element so now we have to find the value k - input[0](that included element) and store that element in o1 output array and in second recursive call as we are not including the element so just directily pass the call by input+1 and size-1 with o2 output array and the same value k.
if(n == 0){ //base case
if(k==0){
output[0][0] = 0;
return 1;
}else{
return 0;
}
}
int o1[1000][50]; //to store the output individually of two recusive calls
int o2[1000][50];
int count1 = subsetSumToK(input+1,n-1,o1,k-input[0]); //recursive calls
int count2 = subsetSumToK(input+1,n-1,o2,k);
for(int i=0;i<count1;i++){ //small calulations
for(int j=1;j<=o1[i][0];j++){
output[i][j+1] = o1[i][j];
}
output[i][0] = o1[i][0] +1;
output[i][1] = input[0];
}
for(int i=0 ; i<count2 ; i++){
for(int j=0 ; j<=o2[i][0] ; j++){
output[i + count1][j] = o2[i][j];
}
}
return count1 + count2;
}

Filter a vector and return the 'n'th element

I want to write a function that evaluates lazily and returns the nth element of a filtered vector. I am interested in the first or second element in the vector most of the times. So I don't want to filter the entire list and then find the nth element.
I am learning to use Boost and if there is a simple solution using Boost it would be highly instructive.
int main() {
double x[] = {10, 12.5, 12.9, 13.7, 50.07};
size_t length = sizeof(x)/sizeof(x[0]);
std::vector<double> vx(x, x+length);
// Need a function to filter out elements less than 11 and return the 2nd
// element greater than 11 (here 12.9) and return without evaluating 13.7 and 50.07
return 0;
}
If you are interested in one of the first elements I would naively suggest something like this:
std::vector<double>::iterator element(size_t n, const std::vector<double>& vec) {
std::vector<double>::iterator it = vec.begin();
size_t found = 0;
while (found != n && it != vec.end()) {
if (*(it++) >= 11) ++found;
}
return it;
}
I has a linear complexity but exits as soon as the desired match is found.
Something like this using std::partition
float n=11.f;
auto it =std::partition(vx.begin(), vx.end(),
[n](const double & p){ return p <n;});
it++; //Second element
if( it!= vx.end())
std::cout<<"Element :"<<*it<<std::endl;
See here
I dont know how to use boost but this is what I would use:
int binarySearch(int whatToSearch){
//recursive binary search
if value is found
return index
else //not found
return -index-1; //-index+1 will give you the place to insert that element
}
Then I would get the second element after returned index if it exists.
Here is the code for binary search that I use
int mybinary_search(string array[],int first,int last, string search_key){
int index;
if (first > last)
index = -first-1;
else{
int mid = (first + last)/2;
if (search_key == array[mid])
return mid;
else if (search_key < array[mid])
index = mybinary_search(array,first, mid-1, search_key);
else
index = mybinary_search(array, mid+1, last, search_key);
} // end if
return index;
}
double findAskedValue(int value){
double x[] = {10, 12.5, 12.9, 13.7, 50.07};
size_t length = sizeof(x)/sizeof(x[0]);
int index = mybinarySearch(x,0,length, 11);
if(index >= 0)
cout << "value I'm searching for " << x[index+2] << endl;
else
cout << "value I'm searching for " << x[(-index-1)+2] << endl;
//instead of cout's you can do what ever you want using that index
}
One little explanation: you are going to call findAskedValue(val) and it will return you a value. If value > 0, then the element exist in the list, and index+2 is the location of the value you are searching for, else (-index-1) is the position where to insert that element. -index-1 + 2 is going to give you the second greater element.
The complexity of that is O(log(n))

C++ Recursive Binary Search with Three Parameters

I'm 99% sure my problem is that I'm setting low to zero every start. But I'm not sure how to keep low consistently representative of the low index regardless of the depth of my recursion. If it accurately told me the index of the low index I don't think I would have a problem.
Here's my code so far:
int recBSearch(vector<int> v, int size, int item)
{
int index = size / 2;
int curr = v[index];
int low = 0;
int high = size -1;
if (v[index] == item)
return index;
else if (v[index] > item)
{
high = index;
index = (high+low)/2;
size = high - low;
return recBSearch(v, size, item);
}
else if (v[index] < item)
{
low = index;
index = (high+low)/2;
size = high - low;
return recBSearch(v, size, item);
}
return -1;
}
This won't work when you are trying to search in the upper half of the vector, because what you really need to create is a slice of the vector.
There is already a binary search but if you are determined to write your own, use an iterator-range in the parameters. (You can either pass in two plain iterators or a boost range).
You want -1 if not found else the iterator location, so in your slice (iterator range) you would need to specify a starting index number in case it is found.
You could also pass, as an alternative, the vector (by const reference) and the range in which you wish to search.
Your last line is unreachable. Instead it should be the terminating condition of your recursion before you do any evaluation. (If your range is empty)
The version that would iterate by passing by reference and using index numbers (simplest) would look like this:
int recBSearch( std::vector<int> const& vec, int start, int end, int value )
{
if( start == end )
{
return -1;
}
int index = (start + end) / 2;
// continue from here
}
end would indicate "one past the last element" so if the vector has size 5, the first iteration would pass 0 and 5. If the vector is empty, you pass 0 and 0.
As an exercise, "can it be done with 3 parameters"?
Yes...
typedef std::vector<int>::const_iterator citer;
int recBSearch( citer start, citer end, int value )
{
if( start == end )
{
return -1;
}
citer middle = start + (end-start)/2;
if( *value == *middle )
{
return middle - start;
}
else if ( *value < *middle )
{
return recBSearch( start, middle, value );
}
else // note the change here
{
int res = recBSearch( middle+1, end, value );
if( res == -1 )
return -1;
else
return res + 1 + (middle-start);
}
}
If you want to do it recursive, your method needs to take the search range as parameters. Else you can't keep track of where to search in the rucursive call assuming you always give the full vector to the function.
So your method signature should be like:
int recBSearch(vector<int> v, int first, int last, int item)
Binary search basically works by dividing your range in 2 halves and searching in each one of them. Your code shows that you operate on the lower half for your both branches. You need to pass to the recursive call the higher half of your v vector in the second else if as well as size/2 instead of size.

Determining the individual letters of Fibonacci strings?

The Fibonacci strings are defined as follows:
The first Fibonacci string is "a"
The second Fibonacci string is "bc"
The (n + 2)nd Fibonacci string is the concatenation of the two previous Fibonacci strings.
For example, the first few Fibonacci strings are
a
bc
abc
bcabc
abcbcabc
The goal is, given a row and an offset, to determine what character is at that offset. More formally:
Input: Two integers separated by a space - K and P(0 < K ≤ 109), ( < P ≤ 109), where K is the line number of the Fibonacci string and P is the position number in a row.
Output: The desired character for the relevant test: "a", "b" or "c". If P is greater than the kth row (K ≤ 109), it is necessary to derive «No solution»
Example:
input: 18 58
output: a
I wrote this code to solve the problem:
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int main()
{
int k, p;
string s1 = "a";
string s2 = "bc";
vector < int >fib_numb;
fib_numb.push_back(1);
fib_numb.push_back(2);
cin >> k >> p;
k -= 1;
p -= 1;
while (fib_numb.back() < p) {
fib_numb.push_back(fib_numb[fib_numb.size() - 1] + fib_numb[fib_numb.size() - 2]);
}
if (fib_numb[k] <= p) {
cout << "No solution";
return 0;
}
if ((k - fib_numb.size()) % 2 == 1)
k = fib_numb.size() + 1;
else
k = fib_numb.size();
while (k > 1) {
if (fib_numb[k - 2] > p)
k -= 2;
else {
p -= fib_numb[k - 2];
k -= 1;
}
}
if (k == 1)
cout << s2[p];
else
cout << s1[0];
return 0;
}
Is it correct? How would you have done?
You can solve this problem without explicitly computing any of the strings, and this is probably the best way to solve the problem. After all, if you're asked to compute the 50th Fibonacci string, you're almost certain to run out of memory; F(50) is 12,586,269,025, so you'd need over 12Gb of memory just to hold it!
The intuition behind the solution is that because each line of the Fibonacci strings are composed of the characters of the previous lines, you can convert an (row, offset) pair into a different (row', offset') pair where the new row is always for a smaller Fibonacci string than the one you started with. If you repeat this enough times, eventually you will arrive back at the Fibonacci strings for either row 0 or row 1, in which case the answer can immediately be read off.
In order to make this algorithm work, we need to establish a few facts. First, let's define the Fibonacci series to be zero-indexed; that is, the sequence is
F(0) = 0
F(1) = 1
F(n+2) = F(n) + F(n + 1)
Given this, we know that the nth row (one-indexed) of the Fibonacci strings has a total of F(n + 1) characters in it. You can see this quickly by induction:
Row 1 has length 1 = F(2) = F(1 + 1)
Row 2 has length 2 = F(3) = F(2 + 1).
For some row n + 2, the length of that row is given by Size(n) + Size(n + 1) = F(n + 1) + F(n + 2) = F(n + 3) = F((n + 2) + 1)
Using this knowledge, let's suppose that we want to find the seventh character of the seventh row of the Fibonacci strings. We know that row seven is composed of the concatenation of rows five and six, so the string looks like this:
R(7) = R(5) R(6)
Row five has F(5 + 1) = F(6) = 8 characters in it, which means that the first eight characters of row seven come from R(5). Since we want the seventh character out of this row, and since 7 ≤ 8, we know that we now need to look at the seventh character of row 5 to get this value. Well, row 5 looks like the concatenation of rows 3 and 4:
R(5) = R(3) R(4)
We want to find the seventh character of this row. Now, R(3) has F(4) = 3 characters in it, which means that if we are looking for the seventh character of R(5), it's going to be in the R(4) part, not the R(3) part. Since we're looking for the seventh character of this row, it means that we're looking for the the 7 - F(4) = 7 - 3 = 4th character of R(4), so now we look there. Again, R(4) is defined as
R(4) = R(2) R(3)
R(2) has F(3) = 2 characters in it, so we don't want to look in it to find the fourth character of the row; that's going to be contained in R(3). The fourth character of the line must be the second character of R(3). Let's look there. R(3) is defined as
R(3) = R(1) R(2)
R(1) has one character in it, so the second character of this line must be the first character of R(1), so we look there. We know, however, that
R(2) = bc
So the first character of this string is b, which is our answer. Let's see if this is right. The first seven rows of the Fibonacci strings are
1 a
2 bc
3 abc
4 bcabc
5 abcbcabc
6 bcabcabcbcabc
7 abcbcabcbcabcabcbcabc
Sure enough, if you look at the seventh character of the seventh string, you'll see that it is indeed a b. Looks like this works!
More formally, the recurrence relation we are interested in looks like this:
char NthChar(int row, int index) {
if (row == 1) return 'a';
if (row == 2 && index == 1) return 'b';
if (row == 2 && index == 2) return 'c';
if (index < Fibonacci(row - 1)) return NthChar(row - 2, index);
return NthChar(row - 1, index - Fibonacci(row - 1));
}
Now, of course, there's a problem with the implementation as written here. Because the row index can range up to 109, we can't possibly compute Fibonacci(row) in all cases; the one billionth Fibonacci number is far too large to represent!
Fortunately, we can get around this. If you look at a table of Fibonacci numbers, you'll find that F(45) = 1,134,903,170, which is greater than 109 (and no smaller Fibonacci number is greater than this). Moreover, since we know that the index we care about must also be no greater than one billion, if we're in row 46 or greater, we will always take the branch where we look in the first half of the Fibonacci string. This means that we can rewrite the code as
char NthChar(int row, int index) {
if (row == 1) return 'a';
if (row == 2 && index == 1) return 'b';
if (row == 2 && index == 2) return 'c';
/* Avoid integer overflow! */
if (row >= 46) return NthChar(row - 2, index);
if (index < Fibonacci(row - 1)) return NthChar(row - 2, index);
return NthChar(row - 1, index - Fibonacci(row - 1));
}
At this point we're getting very close to a solution. There are still a few problems to address. First, the above code will almost certainly blow out the stack unless the compiler is good enough to use tail recursion to eliminate all the stack frames. While some compilers (gcc, for example) can detect this, it's probably not a good idea to rely on it, and so we probably should rewrite this recursive function iteratively. Here's one possible implementation:
char NthChar(int row, int index) {
while (true) {
if (row == 1) return 'a';
if (row == 2 && index == 1) return 'b';
if (row == 2 && index == 2) return 'c';
/* Avoid integer overflow! */
if (row >= 46 || index < Fibonacci(row - 1)) {
row -= 2;
} else {
index -= Fibonacci(row - 1);
row --;
}
}
}
But of course we can still do much better than this. In particular, if you're given a row number that's staggeringly huge (say, one billion), it's really silly to keep looping over and over again subtracting two from the row until it becomes less than 46. It makes a lot more sense to just determine what value it's ultimately going to become after we do all the subtraction. But we can do this quite easily. If we have an even row that's at least 46, we'll end up subtracting out 2 until it becomes 44. If we have an odd row that's at least 46, we'll end up subtracting out 2 until it becomes 45. Consequently, we can rewrite the above code to explicitly handle this case:
char NthChar(int row, int index) {
/* Preprocess the row to make it a small value. */
if (row >= 46) {
if (row % 2 == 0)
row = 45;
else
row = 44;
}
while (true) {
if (row == 1) return 'a';
if (row == 2 && index == 1) return 'b';
if (row == 2 && index == 2) return 'c';
if (index < Fibonacci(row - 1)) {
row -= 2;
} else {
index -= Fibonacci(row - 1);
row --;
}
}
}
There's one last thing to handle, which is what happens if there isn't a solution to the problem because the character is out of range. But we can easily fix this up:
string NthChar(int row, int index) {
/* Preprocess the row to make it a small value. */
if (row >= 46) {
if (row % 2 == 0)
row = 45;
else
row = 44;
}
while (true) {
if (row == 1 && index == 1) return "a"
if (row == 2 && index == 1) return "b";
if (row == 2 && index == 2) return "c";
/* Bounds-checking. */
if (row == 1) return "no solution";
if (row == 2) return "no solution";
if (index < Fibonacci(row - 1)) {
row -= 2;
} else {
index -= Fibonacci(row - 1);
row --;
}
}
}
And we've got a working solution.
One further optimization you might do is precomputing all of the Fibonacci numbers that you'll need and storing them in a giant array. You only need Fibonacci values for F(2) through F(44), so you could do something like this:
const int kFibonacciNumbers[45] = {
0, 1, 1, 2, 3, 5,
8, 13, 21, 34, 55, 89,
144, 233, 377, 610,
987, 1597, 2584, 4181,
6765, 10946, 17711, 28657,
46368, 75025, 121393, 196418,
317811, 514229, 832040,
1346269, 2178309, 3524578,
5702887, 9227465, 14930352,
24157817, 39088169, 63245986,
102334155, 165580141, 267914296,
433494437, 701408733
};
With this precomputed array, the final version of the code would look like this:
string NthChar(int row, int index) {
/* Preprocess the row to make it a small value. */
if (row >= 46) {
if (row % 2 == 0)
row = 45;
else
row = 44;
}
while (true) {
if (row == 1 && index == 1) return "a"
if (row == 2 && index == 1) return "b";
if (row == 2 && index == 2) return "c";
/* Bounds-checking. */
if (row == 1) return "no solution";
if (row == 2) return "no solution";
if (index < kFibonacciNumbers[row - 1]) {
row -= 2;
} else {
index -= kFibonacciNumbers[row - 1];
row --;
}
}
}
I have not yet tested this; to paraphrase Don Knuth, I've merely proved it correct. :-) But I hope this helps answer your question. I really loved this problem!
I guess your general idea should be OK, but I don't see how your code is going to deal with larger values of K, because the numbers will get enormous quickly, and even with large integer libraries it might take virtually forever to compute fibonacci(10^9) exactly.
Fortunately, you are only asked about the first 10^9 characters. The string will reach that many characters already on the 44th line (f(44) = 1134903170).
And if I'm not mistaken, from there on the first 10^9 characters will be simply alternating between the prefixes of line 44 and 45, and therefore in pseudocode:
def solution(K, P):
if K > 45:
if K % 2 == 0:
return solution(44, P)
else:
return solution(45, P)
#solution for smaller values of K here
I found this. I did not do a pre-check (get the size of the k-th fibo string to test p againt it) because if the check is successful you'll have to compute it anyway. Of course as soon as k becomes big, you may have an overflow issue (the length of the fibo string is an exponential function of the index n...).
#include <iostream>
#include <string>
using namespace std;
string fibo(unsigned int n)
{
if (n == 0)
return "a";
else if (n == 1)
return "bc";
else
return fibo(n - 2) + fibo(n - 1);
}
int main()
{
unsigned int k, p;
cin >> k >> p;
--k;
--p;
string fiboK = fibo(k);
if (p > fiboK.size())
cout << "No solution" << endl;
else
cout << fiboK[p] << endl;
return 0;
}
EDIT: ok, I now see your point, i.e. checking in which part of the k-th string p resides (i.e. in string k - 2 or k - 1, and updating p if needed). Of course this is the good way to do it, since as I was saying above my naive solution will explode quite too quickly.
Your way looks correct to me from an algorithm point of view (saves memory and complexity).
I would have computed the K-th Fibonacci String, and then retrieve the P-th character of it. Something like that:
#include <iostream>
#include <string>
#include <vector>
std::string FibonacciString(unsigned int k)
{
std::vector<char> buffer;
buffer.push_back('a');
buffer.push_back('b');
buffer.push_back('c');
unsigned int curr = 1;
unsigned int next = 2;
while (k --)
{
buffer.insert(
buffer.end(),
buffer.begin(),
buffer.end());
buffer.erase(
buffer.begin(),
buffer.begin() + curr);
unsigned int prev = curr;
curr = next;
next = prev + next;
}
return std::string(
buffer.begin(),
buffer.begin() + curr);
}
int main(int argc, char** argv)
{
unsigned int k, p;
std::cin >> k >> p;
-- p;
-- k;
std::string fiboK = FibonacciString(k);
if (p > fiboK.size())
std::cout << "No solution";
else
std::cout << fiboK[p];
std::cout << std::endl;
return 0;
}
It does use more memory than your version since it needs to store both the N-th and the (N+1)-th Fibonacci string at every instant. However, since it is really close to the definition, it does work for every value.
Your algorithm seems to have some issue when k is large while p is small. The test fib_num[k] < p will dereference an item outside of the range of the array with k = 30 and p = 1, won't it ?
I made another example where each corresponding number of Fibonnaci series corresponds to the letter in the alfabet. So for 1 is a, for 2 is b, for 3 is c, for 5 is e... etc:
#include <iostream>
#include <string>
using namespace std;
int main()
{
string a = "abcdefghijklmnopqrstuvwxyz"; //the alphabet
string a1 = a.substr(0,0); string a2 = a.substr(1,1); string nexT = a.substr(0,0);
nexT = a1 + a2;
while(nexT.length() <= a.length())
{
//cout << nexT.length() << ", "; //show me the Fibonacci numbers
cout << a.substr(nexT.length()-1,1) << ", "; //show me the Fibonacci letters
a1 = a2;
a2 = nexT;
nexT = a1 + a2;
}
return 0;
}
Output: a, b, c, e, h, m, u,
Quote from Wikipedia, Fibonacci_word:
The nth digit of the word is 2+[nφ]-[(n+1)φ] where φ is the golden ratio ...
(The only characters used in the Wikipedia page are 1 and 0.)
But note that the strings in the Wikipedia page, and in Knuth s Fundamental Algorithms, are built up in the opposite order of the above shown strings; there it becomes clear when the strings are listed, with ever repeating leading part, that there is only one infinitely long Fibonacci string. It is less clear when generated in the above used order, for the ever repeating part is the string s trailing part, but it is no less true. Therefore the term "the word" in the quotation, and, except for the question "is n too great for this row?", the row is not important.
Unhappily, though, it is too hard to apply this formula to the poster s problem, because in this formula the original strings are of the same length, and poster began with "a" and "bc".
This J(ava)Script script generates the Fibonacci string over the characters the poster chose, but in the opposite order. (It contains the Microsoft object WScript used for fetching command-line argument and outputting to the standard output.)
var u, v /*Fibonacci numbers*/, g, i, k, R;
v = 2;
u = 1;
k = 0;
g = +WScript.arguments.item(0); /*command-line argument for desired length of string*/
/*Two consecutiv Fibonacci numbers, with the greater no less than the
Fibonacci string s length*/
while (v < g)
{ v += u;
u = v - u;
k = 1 - k;
}
i = u - k;
while (g-- > 0)
{ /*In this operation, i += u with i -= v when i >= v (carry),
since the Fibonacci numbers are relativly prime, i takes on
every value from 0 up to v. Furthermore, there are u carries,
and, therefore, u instances of character 'cb', and v-u instances
of 'a' (no-carry). The characters are spread as evenly as can be.*/
if ((i += u) < v)
{ R = 'a'; // WScript.StdOut.write('a'); /* no-carry */
} else
{ i -= v; /* carry */
R = 'cb'; // WScript.StdOut.write('cb')
}
}
/*result is in R*/ // WScript.StdOut.writeLine()
I suggest it because actually outputting the string is not required. One can simply stop at the desired length, and show the last thing about to be outputted. (The code for output is commented out with '//'). Of course, using this to find the character at position n has cost proportional to n. The formula at the top costs much less.