Related
I am trying to solve a problem where every letter has a respective number such as a-1,b-2....z-26.
Now given a number, in how many ways can the number be decoded is the question. consider an example where 25114 can be decoded as 'BEAN',‘BEAAD’, ‘YAAD’, ‘YAN’, ‘YKD’ and ‘BEKD’. this could be decoded in 6 ways.
I have written code in c++ but I am getting the wrong answer. Please correct my code.
#include<bits/stdc++.h>
using namespace std;
int total = 0;
int arr[100001];
void func(int start,int end,int factor){
if(start==end)
return;
int j =start;
if(factor==2&&j==end-1)//if j is the last element and factor is 2,accessing j+1 element is illegual
return;
if(factor==2){
if((arr[j]*10+arr[j+1])>26)
return;
else{
total++;
func(start+2,end,1);
func(start+2,end,2);
}
}
else{//factor is 1
total++;
func(start+1,end,1);
func(start+1,end,2);
}
}
int main(){
int n;
cin>>n;
for(int i=0;i<n;i++){
int p;
cin>>p;
arr[i]=p;
}
func(0,n,1);
func(0,n,2);
cout<<total<<endl;
return 0;
}
essentially what my code is doing is that it fixes one number from the given array(using one digit or two digits from the the given array) and recurses until all the combinations are covered. for example considering the above case, I first choose '2' as my first digit and decode it as 'B'(factor = 1) and then choose '25' and decode it as 'E'(factor = 2).
**following are the input and output from the following code
input : 25114
expected output : 6
my output : 15
input : 3333333333(10 digits)
expected output : 1
my output : 10
Based on the original program from the question I suggest to count the encodings when you reach the end only (if(start==end)).
As func will always be called twice with factor=1 and factor=2, I can freely choose either condition for counting.
Here is the modified code:
#include<bits/stdc++.h>
using namespace std;
int total = 0;
int arr[100001];
void func(int start,int end,int factor){
if(start==end) {
if(factor == 1) total++; // count once when reaching the end
return;
}
int j =start;
if((factor==2) && (j==end-1))//if j is the last element and factor is 2,accessing j+1 element is illegal
return;
if(factor==2){
if((arr[j]*10+arr[j+1])>26)
return;
else{
//total++;
func(start+2,end,1);
func(start+2,end,2);
}
}
else{//factor is 1
//total++;
func(start+1,end,1);
func(start+1,end,2);
}
return;
}
int main(){
int n;
cin>>n;
for(int i=0;i<n;i++){
int p;
cin>>p;
arr[i]=p;
}
func(0,n,1);
func(0,n,2);
cout<<total<<endl;
return 0;
}
This calculates the expected results from the example input in the question.
$ echo 5 2 5 1 1 4|./program
6
$ echo 10 3 3 3 3 3 3 3 3 3 3|./program
1
There is room for improvement.
Instead of modifying a global variable I would return the number of combinations from func and add the values in the higher level.
I would also handle the distinction between 2-digit and 1-digit numbers in the called func instead of in the caller.
Something like this pseudo code:
int func(int start, int end)
{
if(remaining length is <2) {
// we reached the end, so this is one combination
return 1;
}
if(two-digit number is >26) {
// only a 1-digit number is possible, count remaining combinations
return func(start+1, end);
}
// both a 1-digit or 2-digit number is possible, add the remaining combinations for both cases
return func(start+1) + func(start+2);
}
Your question is tagged as "dynamic-programming", but it is anything but.
Instead, think about the state space and its boundary conditions:
The empty string has zero encodings;
A single digit has a single encoding;
An n-digit string has as many encodings as an (n-1)-digit substring plus as many encodings as an (n-2)-digit substring if the first two digits are <= 26.
Thus, we can walk the string from back to front and store the intermediate results for reuse:
uint64_t solve(std::vector<int>& digits) {
const int n = digits.size();
std::vector<int> encodings(n+1);
encodings[n] = 1;
for (int i = n-1; i >= 0; i--) {
bool two_digits_fit = (i < n - 1) && (digits[i] * 10 + digits[i+1]) <= 26; // What if digits[i] == 0?
encodings[i] = encodings[i+1] + (two_digits_fit ? encodings[i+2] : 0);
}
return encodings[0];
}
Write a function countMatches that searches the substring in the given string and returns how many times the substring appears in the string.
I've been stuck on this awhile now (6+ hours) and would really appreciate any help I can get. I would really like to understand this better.
int countMatches(string str, string comp)
{
int small = comp.length();
int large = str.length();
int count = 0;
// If string is empty
if (small == 0 || large == 0) {
return -1;
}
// Increment i over string length
for (int i = 0; i < small; i++) {
// Output substring stored in string
for (int j = 0; j < large; j++) {
if (comp.substr(i, small) == str.substr(j, large)) {
count++;
}
}
}
cout << count << endl;
return count;
}
When I call this function from main, with countMatches("Hello", "Hello"); I get the output of 5. Which is completely wrong as it should return 1. I just want to know what I'm doing wrong here so I don't repeat the mistake and actually understand what I am doing.
I figured it out. I did not need a nested for loop because I was only comparing the secondary string to that of the string. It also removed the need to take the substring of the first string. SOOO... For those interested, it should have looked like this:
int countMatches(string str, string comp)
{
int small = comp.length();
int large = str.length();
int count = 0;
// If string is empty
if (small == 0 || large == 0) {
return -1;
}
// Increment i over string length
for (int i = 0; i < large; i++) {
// Output substring stored in string
if (comp == str.substr(i, small)) {
count++;
}
}
cout << count << endl;
return count;
}
The usual approach is to search in place:
std::string::size_type pos = 0;
int count = 0;
for (;;) {
pos = large.find(small, pos);
if (pos == std::string::npos)
break;
++count;
++pos;
}
That can be tweaked if you're not concerned about overlapping matches (i.e., looking for all occurrences of "ll" in the string "llll", the answer could be 3, which the above algorithm will give, or it could be 2, if you don't allow the next match to overlap the first. To do that, just change ++pos to pos += small.size() to resume the search after the entire preceding match.
The problem with your function is that you are checking that:
Hello is substring of Hello
ello is substring of ello
llo is substring of llo
...
of course this matches 5 times in this case.
What you really need is:
For each position i of str
check if the substring of str starting at i and of length = comp.size() is exactly comp.
The following code should do exactly that:
size_t countMatches(const string& str, const string& comp)
{
size_t count = 0;
for (int j = 0; j < str.size()-comp.size()+1; j++)
if (comp == str.substr(j, comp.size()))
count++;
return count;
}
So, this is an interview question that I was going through.
I have strings a, b, and c. I want to obtain string k by swapping some letters in a, so that k should contain as many non-overlapping substrings equal either to b or c as possible. Substring of string x is a string formed by consecutive segment of characters from x. Two substrings of string x overlap if there is position i in string x occupied by both of them.
Input: The first line contains string a, the second line contains string b, and the third line contains string c (1 ≤ |a|, |b|, |c| ≤ 10^5, where |s| denotes the length of string s).
All three strings consist only of lowercase English letters.
It is possible that b and c coincide.
Output: Find one of possible strings k.
Example:
I/P
abbbaaccca
ab
aca
O/P
ababacabcc
this optimal solutions has three non-overlaping substrings equal to either b or c on positions 1 – 2 (ab), 3 – 4 (ab), 5 – 7 (aca).
Now, the approach that I could think of was to make a character count array for each of the strings, and then proceed ahead. Basically, iterate over the original string (a), check for occurences of b and c. If not there, swap as many characters as possible to make either b or c (whichever is shorter). But, clearly this is not the optimal approach.
Can anyone suggest something better? (Only pseudocode will be enough)
Thanks!
First thing is you'll need to do is count the number of occurrences of each character of each string. The occurrences count of a will be your knapsack, whom you'll need to fill with as many b's or c's.
Note that when I say knapsack I mean the character count vector of a, and inserting b to a will mean reducing the character count vector of a by the character count vector of b.
I'm a little bit short with my mathematical prove, but you'll need to
insert as many b as possible to the knapsack
Insert as many c as possible to the knapsack (in the space that left after 1).
If a removal of a b from the knapsack will enable an insertion of more c, remove b from the knapsack. Otherwise, finish.
Fill as many c that you can to the knapsack
Repeat 3-4.
Throughout the program count the number of b and c in the knapsack and the output should be:
[b_count times b][c_count times c][char_occurrence_left_in_knapsack_for_char_x times char_x for each char_x in lower_case_english]
This should solve your problem at O(n).
Assuming that allowed characters have ASCII code 0-127, I would write a function to count the occurence of each character in a string:
int[] count(String s) {
int[] res = new int[128];
for(int i=0; i<res.length(); i++)
res[i] = 0;
for(int i=0; i<a.length(); i++)
res[i]++;
return res;
}
We can now count occurrences in each string:
int aCount = count(a);
int bCount = count(b);
int cCount = count(c);
We can then write a function to count how many times a string can be carved out of characters of another string:
int carveCount(int[] strCount, int[] subStrCount) {
int min = Integer.MAX_VALUE;
for(int i=0; i<subStrCount.length(); i++) {
if (subStrCount[i] == 0)
continue;
if (strCount[i] >= subStrCount[i])
min = Math.min(min, strCount[i]-subStrCount[i]);
else {
return 0;
}
}
for(int i=0; i<subStrCount.length(); i++) {
if (subStrCount[i] != 0)
strStrCount[i] -= min;
}
return min;
}
and call the function:
int bFitCount = carve(aCount, bCount);
int cFitCount = carve(aCount, cCount);
EDIT: I didn't realize you wanted all characters originally in a, fixing here.
Finally, to produce the output:
StringBuilder sb = new StringBuilder();
for(int i=0; i<bFitCount; i++) {
sb.append(b);
for(int i=0; i<cFitCount; i++) {
sb.append(c);
for(int i=0; i<aCount.length; i++) {
for(int j=0; j<aCount[i]; j++)
sb.append((char)i);
}
return sb.toString();
One more comment: if the goal is to maximize the number of repetitions(b)+repetitions(c), then you may want to first swab b and c if c is shorter. This way if they share some characters you have better chance of increasing the result.
The algorithm could be optimized further, but as it is it should have complexity O(n), where n is the sum of the length of the three strings.
A related problem is called Knapsack problem.
This is basically the solution described by #Tal Shalti.
I tried to keep everything readable.
My program return abbcabacac as one of the string with the most occurences (3).
To get all permutations without repeating a permutation I use std::next_permutation from algorithm. There not much happening in the main function. I only store the number of occurrences and the permutation, if a higher number of occurrences was achieved.
int main()
{
std::string word = "abbbaaccca";
std::string patternSmall = "ab";
std::string patternLarge = "aca";
unsigned int bestOccurrence = 0;
std::string bestPermutation = "";
do {
// count and remove occurrence
unsigned int occurrences = FindOccurences(word, patternLarge, patternSmall);
if (occurrences > bestOccurrence) {
bestOccurrence = occurrences;
bestPermutation = word;
std::cout << word << " .. " << occurences << std::endl;
}
} while (std::next_permutation(word.begin(), word.end()));
std::cout << "Best Permutation " << bestPermutation << " with " << bestOccurrence << " occurrences." << std::endl;
return 0;
}
This function handles the basic algorithm. pattern1 is the longer pattern, so it will be searched for last. If a pattern is found, it will be replaced with the string "##", since this should be very rare in the English language.
The variable occurrenceCounter keeps track of the number of found occurences.
unsigned int FindOccurrences(const std::string& word, const std::string& pattern1, const std::string& pattern2)
{
unsigned int occurrenceCounter = 0;
std::string tmpWord(word);
// '-1' makes implementation of while() easier
std::string::size_type i = -1;
i = -1;
while (FindPattern(tmpWord, pattern2, ++i)) {
occurrenceCounter++;
tmpWord.replace(tmpWord.begin() + i, tmpWord.begin() + i + pattern2.size(), "##");
}
i = -1;
while (FindPattern(tmpWord, pattern1, ++i)) {
occurrenceCounter++;
tmpWord.replace(tmpWord.begin() + i, tmpWord.begin() + i + pattern1.size(), "##");
}
return occurrenceCounter;
}
This function returns the first position of the found pattern. If the pattern is not found, std::string::npos is returned by string.find(...). Also string.find(...) starts to search for the pattern starting by index i.
bool FindPattern(const std::string& word, const std::string& pattern, std::string::size_type& i)
{
std::string::size_type foundPosition = word.find(pattern, i);
if (foundPosition == std::string::npos) {
return false;
}
i = foundPosition;
return true;
}
I was solving a question, with which I am having some problems:
Complete the function getEqualSumSubstring, which takes a single argument. The single argument is a string s, which contains only non-zero digits.
This function should print the length of longest contiguous substring of s, such that the length of the substring is 2*N digits and the sum of the leftmost N digits is equal to the sum of the rightmost N digits. If there is no such string, your function should print 0.
int getEqualSumSubstring(string s) {
int i=0,j=i,foundLength=0;
for(i=0;i<s.length();i++)
{
for(j=i;j<s.length();j++)
{
int temp = j-i;
if(temp%2==0)
{
int leftSum=0,rightSum=0;
string tempString=s.substr(i,temp);
for(int k=0;k<temp/2;k++)
{
leftSum=leftSum+tempString[k]-'0';
rightSum=rightSum+tempString[k+(temp/2)]-'0';
}
if((leftSum==rightSum)&&(leftSum!=0))
if(s.length()>foundLength)
foundLength=s.length();
}
}
}
return(foundLength);
}
The problem is that this code is working for some samples and not for the others. Since this is an exam type question I don't have the test cases either.
This code works
int getEqualSumSubstring(string s) {
int i=0,j=i,foundLength=0;
for(i=0;i<s.length();i++)
{
for(j=i;j<s.length();j++)
{
int temp = j-i+1;
if(temp%2==0)
{
int leftSum=0,rightSum=0;
string tempString=s.substr(i,temp);
// printf("%d ",tempString.length());
for(int k=0;k<temp/2;k++)
{
leftSum=leftSum+tempString[k]-48;
rightSum=rightSum+tempString[k+(temp/2)]-48;
}
if((leftSum==rightSum)&&(leftSum!=0))
if(tempString.length()>foundLength)
foundLength=tempString.length();
}
}
}
return(foundLength);
}
The temp variable must be j-i+1. Otherwise the case where the whole string is the answer will not be covered. Also, we need to make the change suggested by Scott.
Here's my solution that I can confirm works. The ones above didn't really work for me - they gave me compile errors somehow. I got the same question on InterviewStreet, came up with a bad, incomplete solution that worked for 9/15 of the test cases, so I had to spend some more time coding afterwards.
The idea is that instead of caring about getting the left and right sums (which is what I initially did as well), I will get all the possible substrings out of each half (left and right half) of the given input, sort and append them to two separate lists, and then see if there are any matches.
Why?
Say the strings "423" and "234" have the same sum; if I sorted them, they would both be "234" and thus match. Since these numbers have to be consecutive and equal length, I no longer need to worry about having to add them up as numbers and check.
So, for example, if I'm given 12345678, then on the left side, the for-loop will give me:
[1,12,123,1234,2,23,234,3,34]
And on the right:
[5,56,567,5678,...]
And so forth.
However, I'm only taking substrings of a length of at least 2 into account.
I append each of these substrings, sorted by converting into a character array then converting back into a string, into ArrayLists.
So now that all this is done, the next step is to see if there are identical strings of the same numbers in these two ArrayLists. I simply check each of temp_b's strings against temp_a's first string, then against temp_a's second string, and so forth.
If I get a match (say, "234" and "234"), I'll set the length of those matching substrings as my tempCount (tempCount = 3). I also have another variable called 'count' to keep track of the greatest length of these matching substrings (if this was the first occurrence of a match, then count = 0 is overwritten by tempCount = 3, so count = 3).
As for the odd/even string length with the variable int end, the reason for this is because in the line of code s.length()/2+j, is the length of the input happened to be 11, then:
s.length() = 11
s.length()/2 = 11/5 = 5.5 = 5
So in the for-loop, s.length()/2 + j, where j maxes out at s.length()/2, would become:
5 + 5 = 10
Which falls short of the s.length() that I need to reach for to get the string's last index.
This is because the substring function requires an end index of one greater than what you'd put for something like charAt(i).
Just to demonstrate, an input of "47582139875" will generate the following output:
[47, 457, 4578, 24578, 57, 578, 2578, 58, 258, 28] <-- substrings from left half
[139, 1389, 13789, 135789, 389, 3789, 35789, 789, 5789, 578] <-- substrings from right half
578 <-- the longest one that matched
6 <-- the length of '578' x 2
public static int getEqualSumSubtring(String s){
// run through all possible length combinations of the number string on left and right half
// append sorted versions of these into new ArrayList
ArrayList<String> temp_a = new ArrayList<String>();
ArrayList<String> temp_b = new ArrayList<String>();
int end; // s.length()/2 is an integer that rounds down if length is odd, account for this later
for( int i=0; i<=s.length()/2; i++ ){
for( int j=i; j<=s.length()/2; j++ ){
// only account for substrings with a length of 2 or greater
if( j-i > 1 ){
char[] tempArr1 = s.substring(i,j).toCharArray();
Arrays.sort(tempArr1);
String sorted1 = new String(tempArr1);
temp_a.add(sorted1);
//System.out.println(sorted1);
if( s.length() % 2 == 0 )
end = s.length()/2+j;
else // odd length so we need the extra +1 at the end
end = s.length()/2+j+1;
char[] tempArr2 = s.substring(i+s.length()/2, end).toCharArray();
Arrays.sort(tempArr2);
String sorted2 = new String(tempArr2);
temp_b.add(sorted2);
//System.out.println(sorted2);
}
}
}
// For reference
System.out.println(temp_a);
System.out.println(temp_b);
// If the substrings match, it means they have the same sum
// Keep track of longest substring
int tempCount = 0 ;
int count = 0;
String longestSubstring = "";
for( int i=0; i<temp_a.size(); i++){
for( int j=0; j<temp_b.size(); j++ ){
if( temp_a.get(i).equals(temp_b.get(j)) ){
tempCount = temp_a.get(i).length();
if( tempCount > count ){
count = tempCount;
longestSubstring = temp_a.get(i);
}
}
}
}
System.out.println(longestSubstring);
return count*2;
}
Heres my solution to this question including tests. I've added an extra function just because I feel it makes the solution way easier to read than the solutions above.
#include <string>
#include <iostream>
using namespace std;
int getMaxLenSumSubstring( string s )
{
int N = 0; // The optimal so far...
int leftSum = 0, rightSum=0, strLen=s.size();
int left, right;
for(int i=0;i<strLen/2+1;i++) {
left=(s[i]-int('0')); right=(s[strLen-i-1]-int('0'));
leftSum+=left; rightSum+=right;
if(leftSum==rightSum) N=i+1;
}
return N*2;
}
int getEqualSumSubstring( string s ) {
int maxLen = 0, substrLen, j=1;
for( int i=0;i<s.length();i++ ) {
for( int j=1; j<s.length()-i; j++ ) {
//cout<<"Substring = "<<s.substr(i,j);
substrLen = getMaxLenSumSubstring(s.substr(i,j));
//cout<<", Len ="<<substrLen;
if(substrLen>maxLen) maxLen=substrLen;
}
}
return maxLen;
}
Here are a few tests I ran. Based upon the examples above they seem right.
int main() {
cout<<endl<<"Test 1 :"<<getEqualSumSubstring(string("123231"))<<endl;
cout<<endl<<"Test 2 :"<<getEqualSumSubstring(string("986561517416921217551395112859219257312"))<<endl;
cout<<endl<<"Test 3:"<<getEqualSumSubstring(string("47582139875"))<<endl;
}
Shouldn't the following code use tempString.length() instead of s.length()
if((leftSum==rightSum)&&(leftSum!=0))
if(s.length()>foundLength)
foundLength=s.length();
Below is my code for the question... Thanks !!
public class IntCompl {
public String getEqualSumSubstring_com(String s)
{
int j;
int num=0;
int sum = 0;
int m=s.length();
//calculate String array Length
for (int i=m;i>1;i--)
{
sum = sum + m;
m=m-1;
}
String [] d = new String[sum];
int k=0;
String ans = "NULL";
//Extract strings
for (int i=0;i<s.length()-1;i++)
{
for (j=s.length();j>=i+1;k++,j--)
{
num = k;
d[k] = s.substring(i,j);
}
k=num+1;
}
//Sort strings in such a way that the longest strings precede...
for (int i=0; i<d.length-1; i++)
{
for (int h=1;h<d.length;h++)
{
if (d[i].length() > d[h].length())
{
String temp;
temp=d[i];
d[i]=d[h];
d[h]=temp;
}
}
}
// Look for the Strings with array size 2*N (length in even number) and such that the
//the sum of left N numbers is = to the sum of right N numbers.
//As the strings are already in decending order, longest string is searched first and break the for loop once the string is found.
for (int x=0;x<d.length;x++)
{
int sum1=0,sum2=0;
if (d[x].length()%2==0 && d[x].length()<49)
{
int n;
n = d[x].length()/2;
for (int y=0;y<n;y++)
{
sum1 = sum1 + d[x].charAt(y)-'0';
}
for (int y=n;y<d[x].length();y++)
{
sum2 = sum2 + d[x].charAt(y)-'0';
}
if (sum1==sum2)
{
ans = d[x];
break;
}
}
}
return ans;
}
}
Here is the complete Java Program for this question.
Complexity is O(n^3)
This can however be solved in O(n^2).For O(n^2) complexity solution refer to this link
import java.util.Scanner;
import static java.lang.System.out;
public class SubStringProblem{
public static void main(String args[]){
Scanner sc = new Scanner(System.in);
out.println("Enter the Digit String:");
String s = sc.nextLine();
int n = (new SubStringProblem()).getEqualSumSubString(s);
out.println("The longest Sum SubString is "+n);
}
public int getEqualSumSubString(String s){
int N;
if(s.length()%2==0)
{
//String is even
N = s.length();
}
else{
//String is odd
N=s.length()-1;
}
boolean flag =false;
int sum1,sum2;
do{
for(int k=0;k<=s.length()-N;k++){
sum1=0;
sum2=0;
for(int i =k,j=k+N-1;i<j;i++,j--)
{
sum1=sum1 + Integer.parseInt(s.substring(i,i+1));
sum2+=Integer.parseInt(s.substring(j,j+1));
}
if(sum1==sum2){
return N;
}
}
N-=2;
flag =true;
}while(N>1);
return -1;
}
}
What is your rationale for the number 48 on these two lines?
for(int k=0;k<temp/2;k++)
{
leftSum=leftSum+tempString[k]-48;
rightSum=rightSum+tempString[k+(temp/2)]-48;
}
I am just overly curious and would like to hear the reasoning behind it, because I have a similar solution, but without the 48 and it still works. However, I added the 48 an still got the correct answer.
Simple solution. O(n*n). s - input string.
var longest = 0;
for (var i = 0; i < s.length-1; i++) {
var leftSum = rightSum = 0;
for (var j = i, k = i+1, l = 2; j >=0 && k < s.length; j--, k++, l+=2) {
leftSum += parseInt(s[j]);
rightSum += parseInt(s[k]);
if (leftSum == rightSum && l > longest) {
longest = l;
}
}
}
console.log(longest);
I'm posting this on behalf of a friend since I believe this is pretty interesting:
Take the string "abb". By leaving out
any number of letters less than the
length of the string we end up with 7
strings.
a b b ab ab bb abb
Out of these 4 are palindromes.
Similarly for the string
"hihellolookhavealookatthispalindromexxqwertyuiopasdfghjklzxcvbnmmnbvcxzlkjhgfdsapoiuytrewqxxsoundsfamiliardoesit"
(a length 112 string) 2^112 - 1
strings can be formed.
Out of these how many are
palindromes??
Below there is his implementation (in C++, C is fine too though). It's pretty slow with very long words; he wants to know what's the fastest algorithm possible for this (and I'm curious too :D).
#include <iostream>
#include <cstring>
using namespace std;
void find_palindrome(const char* str, const char* max, long& count)
{
for(const char* begin = str; begin < max; begin++) {
count++;
const char* end = strchr(begin + 1, *begin);
while(end != NULL) {
count++;
find_palindrome(begin + 1, end, count);
end = strchr(end + 1, *begin);
}
}
}
int main(int argc, char *argv[])
{
const char* s = "hihellolookhavealookatthis";
long count = 0;
find_palindrome(s, strlen(s) + s, count);
cout << count << endl;
}
First of all, your friend's solution seems to have a bug since strchr can search past max. Even if you fix this, the solution is exponential in time.
For a faster solution, you can use dynamic programming to solve this in O(n^3) time. This will require O(n^2) additional memory. Note that for long strings, even 64-bit ints as I have used here will not be enough to hold the solution.
#define MAX_SIZE 1000
long long numFound[MAX_SIZE][MAX_SIZE]; //intermediate results, indexed by [startPosition][endPosition]
long long countPalindromes(const char *str) {
int len = strlen(str);
for (int startPos=0; startPos<=len; startPos++)
for (int endPos=0; endPos<=len; endPos++)
numFound[startPos][endPos] = 0;
for (int spanSize=1; spanSize<=len; spanSize++) {
for (int startPos=0; startPos<=len-spanSize; startPos++) {
int endPos = startPos + spanSize;
long long count = numFound[startPos+1][endPos]; //if str[startPos] is not in the palindrome, this will be the count
char ch = str[startPos];
//if str[startPos] is in the palindrome, choose a matching character for the palindrome end
for (int searchPos=startPos; searchPos<endPos; searchPos++) {
if (str[searchPos] == ch)
count += 1 + numFound[startPos+1][searchPos];
}
numFound[startPos][endPos] = count;
}
}
return numFound[0][len];
}
Explanation:
The array numFound[startPos][endPos] will hold the number of palindromes contained in the substring with indexes startPos to endPos.
We go over all pairs of indexes (startPos, endPos), starting from short spans and moving to longer ones. For each such pair, there are two options:
The character at str[startPos] is not in the palindrome. In that case, there are numFound[startPos+1][endPos] possible palindromes - a number that we have calculated already.
character at str[startPos] is in the palindrome (at its beginning). We scan through the string to find a matching character to put at the end of the palindrome. For each such character, we use the already-calculated results in numFound to find number of possibilities for the inner palindrome.
EDIT:
Clarification: when I say "number of palindromes contained in a string", this includes non-contiguous substrings. For example, the palindrome "aba" is contained in "abca".
It's possible to reduce memory usage to O(n) by taking advantage of the fact that calculation of numFound[startPos][x] only requires knowledge of numFound[startPos+1][y] for all y. I won't do this here since it complicates the code a bit.
Pregenerating lists of indices containing each letter can make the inner loop faster, but it will still be O(n^3) overall.
I have a way can do it in O(N^2) time and O(1) space, however I think there must be other better ways.
the basic idea was the long palindrome must contain small palindromes, so we only search for the minimal match, which means two kinds of situation: "aa", "aba". If we found either , then expand to see if it's a part of a long palindrome.
int count_palindromic_slices(const string &S) {
int count = 0;
for (int position=0; position<S.length(); position++) {
int offset = 0;
// Check the "aa" situation
while((position-offset>=0) && (position+offset+1)<S.length() && (S.at(position-offset))==(S.at(position+offset+1))) {
count ++;
offset ++;
}
offset = 1; // reset it for the odd length checking
// Check the string for "aba" situation
while((position-offset>=0) && position+offset<S.length() && (S.at(position-offset))==(S.at(position+offset))) {
count ++;
offset ++;
}
}
return count;
}
June 14th, 2012
After some investigation, I believe this is the best way to do it.
faster than the accepted answer.
Is there any mileage in making an initial traversal and building an index of all occurances of each character.
h = { 0, 2, 27}
i = { 1, 30 }
etc.
Now working from the left, h, only possible palidromes are at 3 and 17, does char[0 + 1] == char [3 -1] etc. got a palindrome. does char [0+1] == char [27 -1] no, No further analysis of char[0] needed.
Move on to char[1], only need to example char[30 -1] and inwards.
Then can probably get smart, when you've identified a palindrome running from position x->y, all inner subsets are known palindromes, hence we've dealt with some items, can eliminate those cases from later examination.
My solution using O(n) memory and O(n^2) time, where n is the string length:
palindrome.c:
#include <stdio.h>
#include <string.h>
typedef unsigned long long ull;
ull countPalindromesHelper (const char* str, const size_t len, const size_t begin, const size_t end, const ull count) {
if (begin <= 0 || end >= len) {
return count;
}
const char pred = str [begin - 1];
const char succ = str [end];
if (pred == succ) {
const ull newCount = count == 0 ? 1 : count * 2;
return countPalindromesHelper (str, len, begin - 1, end + 1, newCount);
}
return count;
}
ull countPalindromes (const char* str) {
ull count = 0;
size_t len = strlen (str);
size_t i;
for (i = 0; i < len; ++i) {
count += countPalindromesHelper (str, len, i, i, 0); // even length palindromes
count += countPalindromesHelper (str, len, i, i + 1, 1); // odd length palindromes
}
return count;
}
int main (int argc, char* argv[]) {
if (argc < 2) {
return 0;
}
const char* str = argv [1];
ull count = countPalindromes (str);
printf ("%llu\n", count);
return 0;
}
Usage:
$ gcc palindrome.c -o palindrome
$ ./palindrome myteststring
EDIT: I misread the problem as the contiguous substring version of the problem. Now given that one wants to find the palindrome count for the non-contiguous version, I strongly suspect that one could just use a math equation to solve it given the number of distinct characters and their respective character counts.
Hmmmmm, I think I would count up like this:
Each character is a palindrome on it's own (minus repeated characters).
Each pair of the same character.
Each pair of the same character, with all palindromes sandwiched in the middle that can be made from the string between repeats.
Apply recursively.
Which seems to be what you're doing, although I'm not sure you don't double-count the edge cases with repeated characters.
So, basically, I can't think of a better way.
EDIT:
Thinking some more,
It can be improved with caching, because you sometimes count the palindromes in the same sub-string more than once. So, I suppose this demonstrates that there is definitely a better way.
Here is a program for finding all the possible palindromes in a string written in both Java and C++.
int main()
{
string palindrome;
cout << "Enter a String to check if it is a Palindrome";
cin >> palindrome;
int length = palindrome.length();
cout << "the length of the string is " << length << endl;
int end = length - 1;
int start = 0;
int check=1;
while (end >= start) {
if (palindrome[start] != palindrome[end]) {
cout << "The string is not a palindrome";
check=0;
break;
}
else
{
start++;
end--;
}
}
if(check)
cout << "The string is a Palindrome" << endl;
}
public String[] findPalindromes(String source) {
Set<String> palindromes = new HashSet<String>();
int count = 0;
for(int i=0; i<source.length()-1; i++) {
for(int j= i+1; j<source.length(); j++) {
String palindromeCandidate = new String(source.substring(i, j+1));
if(isPalindrome(palindromeCandidate)) {
palindromes.add(palindromeCandidate);
}
}
}
return palindromes.toArray(new String[palindromes.size()]);
}
private boolean isPalindrome(String source) {
int i =0;
int k = source.length()-1;
for(i=0; i<source.length()/2; i++) {
if(source.charAt(i) != source.charAt(k)) {
return false;
}
k--;
}
return true;
}
I am not sure but you might try whit fourier. This problem remined me on this: O(nlogn) Algorithm - Find three evenly spaced ones within binary string
Just my 2cents