Spent about an hour and got a working solution that I believe should give an optimal result:
public class PalindromeFinder : IComparer<Tuple<int,string>>
{
public PalindromeFinder()
{
heap = new IntervalHeap<Tuple<int, string>>(this);
handles = new Dictionary<string, IPriorityQueueHandle<Tuple<int, string>>>();
}
private readonly IntervalHeap<Tuple<int, string>> heap;
private readonly Dictionary<string, IPriorityQueueHandle<Tuple<int, string>>> handles;
public int FindMinValue(string startingWord)
{
AddWord(startingWord,0);
while(!heap.IsEmpty)
{
var item = heap.DeleteMin();
var word = item.Item2;
var value = item.Item1;
handles.Remove(word);
if(IsPalindrome(word))
{
return value;
}
for(int i = 0; i<word.Length; i++)
{
//decrease
var decreased = word.ToCharArray();
if (decreased[i] > 'a')
{
decreased[i]--;
AddWord(new string(decreased), value + 4);
}
//switch
for(int j = i+1; j<word.Length; j++)
{
if (word.Count(x => x == word[i]) >= 2 || word.Count(x => x == word[j]) >= 2)
{
var swap = word.ToCharArray();
var tmp = swap[i];
swap[i] = swap[j];
swap[j] = tmp;
AddWord(new string(swap), value + 7);
}
}
//add character before (only add characters already present)
foreach (char c in word.Distinct())
{
var added = word.Insert(i, c.ToString());
AddWord(added, value+12);
}
}
//add character at end of word
foreach (char c in word.Distinct())
{
var added = word + c.ToString();
AddWord(added, value + 12);
}
}
return int.MaxValue;
}
private void AddWord(string word, int value)
{
var min = int.MaxValue;
if(handles.ContainsKey(word))
{
var tuple = heap.Delete(handles[word]);
min = tuple.Item1;
handles.Remove(word);
}
IPriorityQueueHandle<Tuple<int, string>> handle = null;
heap.Add(ref handle,new Tuple<int, string>(Math.Min(min, value), word));
handles[word] = handle;
}
private bool IsPalindrome(string word)
{
int left = 0;
int right = word.Length - 1;
while(left<right)
{
if (word[left] != word[right]) return false;
left++;
right--;
}
return true;
}
public int Compare(Tuple<int, string> x, Tuple<int, string> y)
{
return x.Item1 - y.Item1;
}
}
Uses priority queue implementation from C5 collections. This is basically the brute force best-first search solution to the problem. It runs out of memory pretty fast, and is less efficient than is probably possible. I could fix that by implementing a better data structure like a DAWG, but out of time for now. Maybe later.It does work well for short words, and even for long ones that are within a few steps of a palindrome already.
Edit:
Made some improvements on Peter's suggestion, but the complexity is still too high. On input: "superman", runs for about a minute until it runs out of memory with over 11 million items in heap and a max value of 42 reached. Still a bit off from being a viable general solution.
3Why is it restricted to C and C++? – user unknown – 2012-01-24T15:06:08.063
@userunknown so I can understand the algorithm :) – e-MEE – 2012-01-24T15:13:04.090
And what is the objective winning criteria? – user unknown – 2012-01-24T15:30:26.543
@userunknown sorry, I'm new in codegolf.SE, and I thought that with the code-challenge tag it was obvious, since I don't want to see backtracking algorithms ^^ – e-MEE – 2012-01-24T16:04:30.987
4Meh. Graph search. – Peter Taylor – 2012-01-24T16:16:15.720
2>
how many points for replacing a character? The same as switch? – elssar – 2012-01-24T16:29:10.213
Also, no points for comparing two characters? – elssar – 2012-01-24T16:34:55.960
Can you switch any two characters or just adjacent ones? – hammar – 2012-01-24T17:11:39.697
@hammar you can switch any two characters – e-MEE – 2012-01-24T20:44:49.467
@elssar replacing means that you remove one, then add a new one – e-MEE – 2012-01-24T20:45:27.797
Well I'm replacing with another character of the string, so it's like half a swap. – elssar – 2012-01-25T03:41:48.413