Friday, 10 April 2009

Finding the next palindrome given a number

I recently was looking for websites similar to Project Euler, and came across SPOJ - the Sphere Online Judge. It gives us some problems, then asks us to submit a solution, which it will evaluate and tell if we are worth our salt as programmers ;)
More details here.

There, I came across this problem which looked quite interesting. Basically, the challenge is, given a number n, we've got to find the palindromic number which comes after this.
Some definitions:
Palindromic number: A number that reads the same either way, like 12421, 57375, 8448, 146641, etc.
"comes after this": the palindrome number must be greater than the given number - given 12321, we must find the next palindrome and not return 12321 itself as the answer.

Ok, with those made clear, let's dive into the problem.

On first sight, it seems simple enough: iterate from n+1 checking if each number is a palindrome. Stop when you find a palindrome.
Unfortunately, that strategy is too slow for our purposes - SPOJ clearly states the inputs can be very huge, in which case this brute force would probably not be able to complete it within the given 9 seconds.

Anyway, we are programmers, and must seek more efficient ways when they exist. Here, it does seem there must be a way to find the next palindrome given a number. What would it be?

My first thought was, simply mirror the number around its centre. Then, we'd surely end up with a palindromic number.
What I mean is, given a number 16310, mirror the first half values across the central '3' to give 16361. If the number has an even number of digits, just mirror it about the centre of the number, like 3628 to 3663.

That strategy works as long as the resulting 'mirror-imaged' value is greater than the original number. What if it results in a number less than (or equal to) the original? Say, for 41258, it would give 41214, which is less than the original number. Or 12321, which would give 12321 itself, which is equal to the original number.

So my next strategy was, if simple mirror-imaging fails, increment the middle digit and then do a mirror image. So, 41258 would become 41314. 12321 would be 12421.

But again, there's a problem with this approach. What if the number had even number of digits? There is no middle digit to increment. Let's say the number was 4265. It's simple to see that, instead of the middle digit, if we increment the digit to the left of the middle (2 here), and do the mirror image, we'll have the palindrome we want: 4334 in this case.

So far, so good.

Now, onto a big issue: 9. Yes, the digit nine. We saw that when the simple mirroring fails, we increment the middle (or left to middle) digit. Now, what do we get when we increment 9? 10. Oops! We can't put a 10 where 9 was. Want proof? Try 41968. Simple mirroring would give 41914, no luck there. Incrementing the 9 to 10 would give 411014, not a palindrome even. And also, it's surely false that there's no palindrome between 41,968 and 411,014 (that's between forty one thousand and four hundred and eleven thousand).

So, what do we do? Luckily, something we learnt in elementary school - carrying over - seems to help here too. Wherever the 10 occurred, keep the 0 there itself, carry over the 1 to the previous digit. So here, 41968 would become 42068 which on applying mirroring would give 42024.

Here too, there's a caveat: consider the extreme cases: 99999 would give (with repeated carryover) 1000001, and 9999 would give 100001. In both cases there's an extra 0 hanging in there. So far I've not been able to generalize it, and have been keeping the case of all digits being 9 a special case only. In this special case, I do the usual procedure, then remove one of the 0's from the resulting number. Ugly, but works. :)

EDIT: Turns out if I finish the increment and carrying over parts fully, then mirror image the number according to how many digits it now has, the 9999 cases take care of themselves with the original algo itself - no 'special cases' needed. :)

So, I believe this is the complete algorithm to find the next palindrome given a number. I have not thought through the steps very carefully, so if you see any flaws, kindly point them out...

I'm trying to code this thing in the Lisp programming language, so I'm taking baby steps towards the solution. As of now, I'm yet to implement the carrying over for 9 part alone. And I don't have any idea how complex that is going to be.

Update: I finished coding this in Lisp, but lost the code somewhere. I coded it again in Python now, which is available here. I've put in a lot of comments so as to make it somewhat easily understandable. Hope this helps the doubting Thomases believe. :)

By the way, my GATE solutions blog now has full feed access, which means you can read it fully without leaving the comfort of your RSS reader.

Happy coding :)

24 comments:

Srinath Reddy B said...
This comment has been removed by the author.
Srinath Reddy B said...

how about 100 or 1000,will it give 101 I hope it doesnt it gives 111/1111.

Sundar said...

@Srinath:
In the case of 100 and 1000, the first simple strategy - mirroring the number about its centre - would do the trick. For 100 it would give 101, and for 1000, 1001, which are the correct answers... So yes, it will give the correct answer, not 111 or anything. :)

Srinath Reddy B said...

but then how do you filter tose numbers from others and what about 9 99 999?

Anonymous said...

i'm working on the same problem, the algorithm is clearly flawed...

how about 890?

u'll get 909, skipping over 898...

i'm just 2 steps below breakthrough, i just know it. :)

Anonymous said...

Took me a while but i figured it out...lemme know if your interested in the solution. or figured it out yourself


Regards.

Unknown said...

St0le: I would like to know how you figured it out.

Sundar said...

The first step of the algorithm is to try mirroring the number about it's center. For 890, that step itself would give 898, which is the correct answer.

Only if we get a number smaller than the original number in that step, (for eg: when input is 899), we proceed to the more complicated steps.

The explanation is probably not clear since there are so many people getting confused. I'll include a pseudocode as soon as I get some spare time.

Unknown said...

and how about tests such as "99999"????

Victor Eloy said...

99999 is already a palindrome

Atri said...

thank you.
i got my solution accepted after reading this blog.
u have explained it very nicely.
Thank you :)

Srinath Reddy B said...

#include
#include
#include
#include
#include


int
nin (long long num)
{
int i = 1;
while ((num / 10) != 0)
{
i++;
num = num / 10;
}
return i;
}



char *
ll2s (long long num)
{
int s = nin (num);
int i = 0;
char *arr = (char *) malloc ((s + 1) * sizeof (char));
while (i < s)
{
arr[s - i - 1] = 48 + (num % 10);
num = num / 10;
i++;
}
arr[s] = '\0';
return arr;
}

long long
s2ll (char *arr)
{
int i = 0;
long long val = 0;
while (i < strlen (arr))
{
val = val * 10 + ((char) arr[i] - 48);
i++;
}
return val;
}




void
strncopy (const char *src, char *dest, int start, int n)
{
int i = 0;
while (i < n)
{
dest[i] = src[start + i];
i++;
}
dest[n] = '\0';
}

int
cg (char *v1, char *v2)
{
int i = 0, j = 0;
i = strlen (v1);
while (j < i)
{
if (v1[j] == v2[j])
{
j++;
}
else if (v1[j] < v2[j])
{
return -1;
}
else
{
return 1;
}
}
}

void
strrev (char *src, char *dest)
{
int i = 0;
while (i < strlen (src))
{
dest[i] = src[strlen (src) - 1 - i];
i++;
}
dest[strlen (src)] = '\0';
}


int
cp (char *a)
{
int i = 0;
while (a[i] == a[strlen (a) - i - 1] && (i < (strlen (a) / 2)))
{
i++;
}
if (a[i] != a[strlen (a) - i - 1])
return 0;
else
return 1;
}

char *
ci (char *val)
{
int i = strlen (val), j = 0;
while (i > 0)
{
if (val[j] != '9')
{
break;
}
j++;
i--;
}

if (i == 0)
{
j = strlen (val);
val = realloc (val, j + 1);
val[j] = '\0';
i = 1;

while (i <= j)
{
val[i] = '0';
i++;
}
val[0] = '1';
}

else
{
j = strlen (val);
j--;
while (val[j] == '9')
{
val[j] = '0';
j--;
}
val[j] = val[j] + 1;
}

return val;

}

Srinath Reddy B said...

char *
palin (char *val)
{
int i = 0;
char *v1, *v2;
// scanf ("%lld", &val);
val = ci (val);
i = strlen (val);
char *s1, *s2, *s3, *s4, *s5, *s6;
s3 = val;
s1 = (char *) malloc (sizeof (char) * ((i / 2) + 1));
s2 = (char *) malloc (sizeof (char) * ((i / 2) + 1));
s4 = (char *) malloc (sizeof (char) * ((i / 2) + 1));
strncopy (s3, s2, ceil ((float) i / 2), i / 2);
strncopy (s3, s1, 0, i / 2);
strrev (s1, s4);
v1 = s4;
v2 = s2;
s5 = (char *) malloc (sizeof (char) * strlen (s1) * 2);
s6 = (char *) malloc (sizeof (char) * ((i / 2) + 1));
if (2 * strlen (s1) == strlen (s3))
{
if (cg (v1, v2) == 0)
{
s5 = s3;
}
else if (cg (v1, v2) == 1)
{
strcat (s5, s1);
strcat (s5, s4);
}
else
{
// strncopy (s3, s6, 0, i / 2);
s2 = ci (s1);
// s5 = (char *) malloc (s2, sizeof (char) * i);
strrev (s2, s4);
strcat (s5, s2);
strcat (s5, s4);
}
}
else
{
if (cg (v1, v2) == 0)
{
s5 = s3;
}
else if (cg (v1, v2) == 1)
{
s2 = (char *) realloc (s2, 2 + i / 2);
strncopy (s3, s2, 0, (1 + i / 2));
// s2 = ll2s (s2ll (s2));
strrev (s1, s4);
s5 = (char *) realloc (s5, i);
strcat (s5, s2);
strcat (s5, s4);
}
else
{
s2 = (char *) realloc (s2, 2 + i / 2);
strncopy (s3, s2, 0, (1 + i / 2));
s2 = ci (s2);
strrev (s1, s4);
s5 = (char *) realloc (s5, i);
strcat (s5, s2);
strcat (s5, s4);
}

}

return s5;
}

int main ()
{
int index = 0;

int i = 0, j = 0;
//char* val;
scanf ("%d", &index);
j = i = index;
char *arr[index]; //=(char**)malloc(sizeof(char*)*i);
char *c[index];// = (char **) malloc (sizeof (char *) * i);
i=0;
while (index>0)
{
arr[i]=(char*)malloc(sizeof(char)*1000001);
scanf ("%s", arr[i]);
i++;
index--;
}
i = 0;
while (i < j)
{
printf ("%s\n", palin(arr[i]));
i++;
}
/*
char *a;
a=(char*)malloc(sizeof(char)*1000001);
scanf("%s",a);
printf("%s",palin(a));
*/
}


/******************************
i am sorry for poor documentation..i have written it for codechef

should compile it with following command as i have written it in c99 for making it compatible for long inputs upto 10^10

gcc -std=c99 -lm

the code mainly takes
1st input as number of test cases
and next lines as numbers whose palindromes are to be printed..

Please do report me for bugs

a sample test case is

3
100
253
441
*******************************/

Srinath Reddy B said...

here is a link to the file which prints palindromes ..i have written it in c99 for printing large palindromes upto 10^10

you have to compile it with

"gcc -std=c99 -lm "

http://cid-7fd0c50da02ebc66.office.live.com/embedicon.aspx/.Public/spgn.c


first input is number of test inputs and rest are inputs

here is a sample test case

3
118
921
52354

iSukhmeet said...

#include
#include
#include
using namespace std;

int isPalindrome(string s) {
for (int i = 0, j = s.length() - 1; i < j; i++, j--) {
if (s[i] != s[j]) {
return 0;
}
}
return 1;
}
int main(){
int t;
cin>>t;
for(int j=0;j>n;
for(int i=n+1;i<=1000002;i++){
string s;
int num;
stringstream ss,ss2;
ss<>s;
int r=isPalindrome(s);
if(r==1){cout<<i<<endl;goto out;}
}
out:;
}
system("pause");
return 0;
}

Srinath Reddy B said...

@mani..thats brute force!!

Vineet said...

Thnx i am grateful to you for providing such a marvelous solution to this problem.. The people who r seeking flaws in this solution are those who need spoon feeding .. You have done your job really well..

Vineet said...

Please check this code. i have coded this in turbo c and i am getting correct answers for all cases but still it is being rejected by Code chef due to some "runtime error" ... i will be thankful to you

#include
#include
#include
#define MAXLEN 20
void main()
{
char num[MAXLEN],num2[MAXLEN];
unsigned long int t,len,j;
int i,carry=0;
clrscr();
scanf("%lld",&t);
for(i=1;i<=t;i++)
{
fflush(stdin);
fflush(stdout);
carry=0;
fgets(num,MAXLEN,stdin);

//puts("viny");
strcpy(num2,num);
len=strlen(num)-1;

//puts(num);
for(j=0;j<=len/2-1;j++)
{
//printf("%c",num[j]);
num[len-j-1]= num[j];
}

if(strcmp(num,num2)<=0)
{

if(num[len/2]=='9')
{
if(len%2==0)
j=len/2-1;

for(;j>0||j==0;j--)
{
if(num[j]=='9')
{
num[j]='0';
carry=1;
}
else
{
if(carry==1)
{
num[j]++;
carry=0;
}
else
break;
}

num[len-j-1]=num[j];


}
num[len]='\0';
//printf("%d",carry);
}
else
{
num[len/2]++;
//printf("%lld",len);
if(len%2==0)
{ num[len/2-1]++;
num[len/2]=num[len/2-1];
}


}

}
if(num[0]=='0')
{
num[0]='1';
puts(strcat(num,"1"));
}
else
{

puts(num);
}
//fflush(stdin);

}





getch();
}

Srinath Reddy B said...

Hi Vineet,

http://cid-7fd0c50da02ebc66.office.live.com/embedicon.aspx/.Public/spgn.c



This is C99 code.
gcc -std=c99 -lm

the code mainly takes
1st input as number of test cases
and next lines as numbers whose palindromes are to be printed..

Please do report me for bugs

a sample test case is

3
100
253
441

Sachin Railhan said...

Sorry but there is an error in this program.Try 6336 as input.
You'll get 6336 as output but instead it should give 6446 as output.

Anonymous said...

hey its very easy in case of 999 or 9999. Consider this just add 1 to 999 or 9999, i.e. we get 1000 and 10000. and then just mirror it as u did earlier.
I hope there is nothing wrong in it.
Please comment/suggest/correct me if i m wrong.

Anonymous said...

This works based on the fact that you can compare the mirrored number and the original number(could be done in python, but not in C/C++). You could have posted a more general solution, which involves comparing during mirroring and then doing the "complicated procedure" :P Just saying...

Ankit Behura said...

Hi
I implemented the exact same algorithm in python before stumbling upon your blog, but it exceeds spoj's timelimit of 9sec for the code. Have you been able to submit your code on spoj?!

Ankit Behura said...

Hi,
I implemented the exact same algorithm on python before stumbling upon your blog, but it seems to be exceeding spoj's timilimit for the code. Have you been able to submit your code on spoj?