Related
We are given a string and we have to find out the minimum number of swaps to convert it into a palindrome.
Ex-
Given string: ntiin
Palindrome: nitin
Minimum number of swaps: 1
If it is not possible to convert it into a palindrome, return -1.
I am unable to think of any approach except brute force. We can check on the first and last characters, if they are equal, we check for the smaller substring, and then apply brute force on it. But this will be of a very high complexity, and I feel this question can be solved in another way. Maybe dynamic programming. How to approach it?
First you could check if the string can be converted to a palindrome.
Just have an array of letters (26 chars if all letters are latin lowercase), and count the number of each letter in the input string.
If string length is even, all letters counts should be even.
If string length is odd, all letters counts should be even except one.
This first pass in O(n) will already treat all -1 cases.
If the string length is odd, start by moving the element with odd count to the middle.
Then you can apply following procedure:
Build a weighted graph with the following logic for an input string S of length N:
For every element from index 0 to N/2-1:
- If symmetric element S[N-index-1] is same continue
- If different, create edge between the 2 characters (alphabetic order), or increment weight of an existing one
The idea is that when a weight is even you can do a 'good swap' by forming two pairs in one swap.
When weight is odd, you cannot place two pairs in one swap, your swaps need to form a cycle
1. For instance "a b a b"
One edge between a,b of weight 2:
a - b (2)
Return 1
2. For instance: "a b c b a c"
a - c (1)
b - a (1)
c - b (1)
See the cycle: a - b, b - c, c - a
After a swap of a,c you get:
a - a (1)
b - c (1)
c - b (1)
Which is after ignoring first one and merge 2 & 3:
c - b (2)
Which is even, you get to the result in one swap
Return 2
3. For instance: "a b c a b c"
a - c (2)
One swap and you are good
So basically after your graph is generated, add to the result the weight/2 (integer division e.g. 7/3 = 3) of each edge
Plus find the cycles and add to the result length-1 of each cycle
there is the same question as asked!
https://www.codechef.com/problems/ENCD12
I got ac for this solution
https://www.ideone.com/8wF9DT
//minimum adjacent swaps to make a string to its palindrome
#include<bits/stdc++.h>
using namespace std;
bool check(string s)
{
int n=s.length();
map<char,int> m;
for(auto i:s)
{
m[i]++;
}
int cnt=0;
for(auto i=m.begin();i!=m.end();i++)
{
if(i->second%2)
{
cnt++;
}
}
if(n%2&&cnt==1){return true;}
if(!(n%2)&&cnt==0){return true;}
return false;
}
int main()
{
string a;
while(cin>>a)
{
if(a[0]=='0')
{
break;
}
string s;s=a;
int n=s.length();
//first check if
int cnt=0;
bool ini=false;
if(n%2){ini=true;}
if(check(s))
{
for(int i=0;i<n/2;i++)
{
bool fl=false;
int j=0;
for(j=n-1-i;j>i;j--)
{
if(s[j]==s[i])
{
fl=true;
for(int k=j;k<n-1-i;k++)
{
swap(s[k],s[k+1]);
cnt++;
// cout<<cnt<<endl<<flush;
}
// cout<<" "<<i<<" "<<cnt<<endl<<flush;
break;
}
}
if(!fl&&ini)
{
for(int k=i;k<n/2;k++)
{
swap(s[k],s[k+1]);
cnt++;
}
// cout<<cnt<<" "<<i<<" "<<endl<<flush;
}
}
cout<<cnt<<endl;
}
else{
cout<<"Impossible"<<endl;
}
}
}
Hope it helps!
Technique behind my code is Greedy
first check if palindrome string can exist for the the string and if it can
there would be two cases one is when the string length would be odd then only count of one char has be odd
and if even then no count should be odd
then
from index 0 to n/2-1 do the following
fix this character and search for this char from n-i-1 to i+1
if found then swap from that position (lets say j) to its new position n-i-1
if the string length is odd then every time you encounter a char with no other occurence shift it to n/2th position..
My solution revolves around the palindrome property that first element and last element should match and if their adjacent elements also do not match then its not a palindrome. Keep comparing and swapping till both reach the same element or adjacent elements.
Written solution in java as below:
public static void main(String args[]){
String input = "natinat";
char[] arr = input.toCharArray();
int swap = 0;
int i = 0;
int j = arr.length-1;
char temp;
while(i<j){
if(arr[i] != arr[j]){
if(arr[i+1] == arr[j]){
//swap i and i+1 and increment i, decrement j, swap++
temp = arr[i];
arr[i] = arr[i+1];
arr[i+1] = temp;
i++;j--;
swap++;
} else if(arr[i] == arr[j-1]){
//swap j and j-1 and increment i, decrement j, swap++
temp = arr[j];
arr[j] = arr[j-1];
arr[j-1] = temp;
i++;j--;
swap++;
} else if(arr[i+1] == arr[j-1] && i+1 != j-1){
//swap i and i+1, swap j and j-1 and increment i, decrement j, swap+2
temp = arr[j];
arr[j] = arr[j-1];
arr[j-1] = temp;
temp = arr[i];
arr[i] = arr[i+1];
arr[i+1] = temp;
i++;j--;
swap = swap+2;
}else{
swap = -1;break;
}
} else{
//increment i, decrement j
i++;j--;
}
}
System.out.println("No Of Swaps: "+swap);
}
My solution in java for any type of string i.e Binary String, Numbers
public int countSwapInPalindrome(String s){
int length = s.length();
if (length == 0 || length == 1) return -1;
char[] str = s.toCharArray();
int start = 0, end = length - 1;
int count = 0;
while (start < end) {
if (str[start] != str[end]){
boolean isSwapped = false;
for (int i = start + 1; i < end; i++){
if (str[start] == str[i]){
char temp = str[i];
str[i] = str[end];
str[end] = temp;
count++;
isSwapped = true;
break;
}else if (str[end] == str[i]){
char temp = str[i];
str[i] = str[start];
str[start] = temp;
count++;
isSwapped = true;
break;
}
}
if (!isSwapped) return -1;
}
start++;
end--;
}
return (s.equals(String.valueOf(str))) ? -1 : count;
}
I hope it helps
string s;
cin>>s;
int n = s.size(),odd=0;
vi cnt(26,0);
unordered_map<int,set<int>>mp;
for(int i=0;i<n;i++){
cnt[s[i]-'a']++;
mp[s[i]-'a'].insert(i);
}
for(int i=0;i<26;i++){
if(cnt[i]&1) odd++;
}
int ans=0;
if((n&1 && odd == 1)|| ((n&1) == 0 && odd == 0)){
int left=0,right=n-1;
while(left < right){
if(s[left] == s[right]){
cnt[left]--;
cnt[right]--;
mp[s[left]-'a'].erase(left);
mp[s[right]-'a'].erase(right);
left++;
right--;
}else{
if(cnt[left]&1 == 0){
ans++;
int index = *mp[s[left]-'a'].rbegin();
mp[s[left]-'a'].erase(index);
mp[s[right]-'a'].erase(right);
mp[s[right]-'a'].insert(index);
swap(s[right],s[index]);
cnt[left]-=2;
}else{
ans++;
int index = *mp[s[right]-'a'].begin();
mp[s[right]-'a'].erase(index);
mp[s[left]-'a'].erase(left);
mp[s[left]-'a'].insert(index);
swap(s[left],s[index]);
cnt[right]-=2;
}
left++;
right--;
}
}
}else{
// cout<<odd<<" ";
cout<<"-1\n";
return;
}
cout<<ans<<"\n";
I want to make a dynamic string generator that will generate all possible unique strings from a character set with a dynamic length.
I can make this very easily using for loops but then its static and not dynamic length.
// Prints all possible strings with the length of 3
for a in allowedCharacters {
for b in allowedCharacters {
for c in allowedCharacters {
println(a+b+c)
}
}
}
But when I want to make this dynamic of length so I can just call generate(length: 5) I get confused.
I found this Stackoverflow question But the accepted answer generates strings 1-maxLength length and I want maxLength on ever string.
As noted above, use recursion. Here is how it can be done with C#:
static IEnumerable<string> Generate(int length, char[] allowed_chars)
{
if (length == 1)
{
foreach (char c in allowed_chars)
yield return c.ToString();
}
else
{
var sub_strings = Generate(length - 1, allowed_chars);
foreach (char c in allowed_chars)
{
foreach (string sub in sub_strings)
{
yield return c + sub;
}
}
}
}
private static void Main(string[] args)
{
string chars = "abc";
List<string> result = Generate(3, chars.ToCharArray()).ToList();
}
Please note that the run time of this algorithm and the amount of data it returns is exponential as the length increases which means that if you have large lengths, you should expect the code to take a long time and to return a huge amount of data.
Translation of #YacoubMassad's C# code to Swift:
func generate(length: Int, allowedChars: [String]) -> [String] {
if length == 1 {
return allowedChars
}
else {
let subStrings = generate(length - 1, allowedChars: allowedChars)
var arr = [String]()
for c in allowedChars {
for sub in subStrings {
arr.append(c + sub)
}
}
return arr
}
}
println(generate(3, allowedChars: ["a", "b", "c"]))
Prints:
aaa, aab, aac, aba, abb, abc, aca, acb, acc, baa, bab, bac, bba, bbb, bbc, bca, bcb, bcc, caa, cab, cac, cba, cbb, cbc, cca, ccb, ccc
While you can (obviously enough) use recursion to solve this problem, it quite an inefficient way to do the job.
What you're really doing is just counting. In your example, with "a", "b" and "c" as the allowed characters, you're counting in base 3, and since you're allowing three character strings, they're three digit numbers.
An N-digit number in base M can represent NM different possible values, going from 0 through NM-1. So, for your case, that's limit=pow(3, 3)-1;. To generate all those values, you just count from 0 through the limit, and convert each number to base M, using the specified characters as the "digits". For example, in C++ the code can look like this:
#include <string>
#include <iostream>
int main() {
std::string letters = "abc";
std::size_t base = letters.length();
std::size_t digits = 3;
int limit = pow(base, digits);
for (int i = 0; i < limit; i++) {
int in = i;
for (int j = 0; j < digits; j++) {
std::cout << letters[in%base];
in /= base;
}
std::cout << "\t";
}
}
One minor note: as I've written it here, this produces the output in basically a little-endian format. That is, the "digit" that varies the fastest is on the left, and the one that changes the slowest is on the right.
Given a string S and a set of n substrings. Remove every instance of those n substrings from S so that S is of the minimum length and output this minimum length.
Example 1
S = ccdaabcdbb
n = 2
substrings = ab, cd
Output
2
Explanation:
ccdaabcdbb -> ccdacdbb -> cabb -> cb (length=2)
Example 2
S = abcd
n = 2
substrings = ab,bcd
Output
1
How do I solve this problem ?
A simple Brute-force search algorithm is:
For each substring, try all possible ways to remove it from the string, then recurse.
In Pseudocode:
def min_final_length (input, substrings):
best = len(input)
for substr in substrings:
beg = 0
// find all occurrences of substr in input and recurse
while (found = find_substring(input, substr, from=beg)):
input_without_substr = input[0:found]+input[found+len(substr):len(input)]
best = min(best, min_final_length(input_without_substr,substrings))
beg = found+1
return best
Let complexity be F(S,n,l) where S is the length of the input string, n is the cardinality of the set substrings and l is the "characteristic length" of substrings. Then
F(S,n,l) ~ n * ( S * l + F(S-l,n,l) )
Looks like it is at most O(S^2*n*l).
The following solution would have an complexity of O(m * n) where m = len(S) and n is the number of substring
def foo(S, sub):
i = 0
while i < len(S):
for e in sub:
if S[i:].startswith(e):
S = S[:i] + S[i+len(e):]
i -= 1
break
else: i += 1
return S, i
If you are for raw performance and your string is very large, you can do better than brute force. Use a suffix trie (E.g, Ukkonnen trie) to store your string. Then find each substring (which us done in O(m) time, m being substring length), and store the offsets to the substrings and length in an array.
Then use the offsets and length info to actually remove the substrings by filling these areas with \0 (in C) or another placeholder character. By counting all non-Null characters you will get the minimal length of the string.
This will als handle overlapping substring, e.g. say your string is "abcd", and you have two substrings "ab" and "abcd".
I solved it using trie+dp.
First insert your substrings in a trie. Then define the state of the dp is some string, walk through that string and consider each i (for i =0 .. s.length()) as the start of some substring. let j=i and increment j as long as you have a suffix in the trie (which will definitely land you to at least one substring and may be more if you have common suffix between some substring, for example "abce" and "abdd"), whenever you encounter an end of some substring, go solve the new sub-problem and find the minimum between all substring reductions.
Here is my code for it. Don't worry about the length of the code. Just read the solve function and forget about the path, I included it to print the string formed.
struct node{
node* c[26];
bool str_end;
node(){
for(int i= 0;i<26;i++){
c[i]=NULL;
}
str_end= false;
}
};
class Trie{
public:
node* root;
Trie(){
root = new node();
}
~Trie(){
delete root;
}
};
class Solution{
public:
typedef pair<int,int>ii;
string get_str(string& s,map<string,ii>&path){
if(!path.count(s)){
return s;
}
int i= path[s].first;
int j= path[s].second;
string new_str =(s.substr(0,i)+s.substr(j+1));
return get_str(new_str,path);
}
int solve(string& s,Trie* &t, map<string,int>&dp,map<string,ii>&path){
if(dp.count(s)){
return dp[s];
}
int mn= (int)s.length();
for(int i =0;i<s.length();i++){
string left = s.substr(0,i);
node* cur = t->root->c[s[i]-97];
int j=i;
while(j<s.length()&&cur!=NULL){
if(cur->str_end){
string new_str =left+s.substr(j+1);
int ret= solve(new_str,t,dp,path);
if(ret<mn){
path[s]={i,j};
}
}
cur = cur->c[s[++j]-97];
}
}
return dp[s]=mn;
}
string removeSubstrings(vector<string>& substrs, string s){
map<string,ii>path;
map<string,int>dp;
Trie*t = new Trie();
for(int i =0;i<substrs.size();i++){
node* cur = t->root;
for(int j=0;j<substrs[i].length();j++){
if(cur->c[substrs[i][j]-97]==NULL){
cur->c[substrs[i][j]-97]= new node();
}
cur = cur->c[substrs[i][j]-97];
if(j==substrs[i].length()-1){
cur->str_end= true;
}
}
}
solve(s,t,dp,path);
return get_str(s, path);
}
};
int main(){
vector<string>substrs;
substrs.push_back("ab");
substrs.push_back("cd");
Solution s;
cout << s.removeSubstrings(substrs,"ccdaabcdbb")<<endl;
return 0;
}
This is a question from one of the online coding challenge (which has completed).
I just need some logic for this as to how to approach.
Problem Statement:
We have two strings A and B with the same super set of characters. We need to change these strings to obtain two equal strings. In each move we can perform one of the following operations:
1. swap two consecutive characters of a string
2. swap the first and the last characters of a string
A move can be performed on either string.
What is the minimum number of moves that we need in order to obtain two equal strings?
Input Format and Constraints:
The first and the second line of the input contains two strings A and B. It is guaranteed that the superset their characters are equal.
1 <= length(A) = length(B) <= 2000
All the input characters are between 'a' and 'z'
Output Format:
Print the minimum number of moves to the only line of the output
Sample input:
aab
baa
Sample output:
1
Explanation:
Swap the first and last character of the string aab to convert it to baa. The two strings are now equal.
EDIT : Here is my first try, but I'm getting wrong output. Can someone guide me what is wrong in my approach.
int minStringMoves(char* a, char* b) {
int length, pos, i, j, moves=0;
char *ptr;
length = strlen(a);
for(i=0;i<length;i++) {
// Find the first occurrence of b[i] in a
ptr = strchr(a,b[i]);
pos = ptr - a;
// If its the last element, swap with the first
if(i==0 && pos == length-1) {
swap(&a[0], &a[length-1]);
moves++;
}
// Else swap from current index till pos
else {
for(j=pos;j>i;j--) {
swap(&a[j],&a[j-1]);
moves++;
}
}
// If equal, break
if(strcmp(a,b) == 0)
break;
}
return moves;
}
Take a look at this example:
aaaaaaaaab
abaaaaaaaa
Your solution: 8
aaaaaaaaab -> aaaaaaaaba -> aaaaaaabaa -> aaaaaabaaa -> aaaaabaaaa ->
aaaabaaaaa -> aaabaaaaaa -> aabaaaaaaa -> abaaaaaaaa
Proper solution: 2
aaaaaaaaab -> baaaaaaaaa -> abaaaaaaaa
You should check if swapping in the other direction would give you better result.
But sometimes you will also ruin the previous part of the string. eg:
caaaaaaaab
cbaaaaaaaa
caaaaaaaab -> baaaaaaaac -> abaaaaaaac
You need another swap here to put back the 'c' to the first place.
The proper algorithm is probably even more complex, but you can see now what's wrong in your solution.
The A* algorithm might work for this problem.
The initial node will be the original string.
The goal node will be the target string.
Each child of a node will be all possible transformations of that string.
The current cost g(x) is simply the number of transformations thus far.
The heuristic h(x) is half the number of characters in the wrong position.
Since h(x) is admissible (because a single transformation can't put more than 2 characters in their correct positions), the path to the target string will give the least number of transformations possible.
However, an elementary implementation will likely be too slow. Calculating all possible transformations of a string would be rather expensive.
Note that there's a lot of similarity between a node's siblings (its parent's children) and its children. So you may be able to just calculate all transformations of the original string and, from there, simply copy and recalculate data involving changed characters.
You can use dynamic programming. Go over all swap possibilities while storing all the intermediate results along with the minimal number of steps that took you to get there. Actually, you are going to calculate the minimum number of steps for every possible target string that can be obtained by applying given rules for a number times. Once you calculate it all, you can print the minimum number of steps, which is needed to take you to the target string. Here's the sample code in JavaScript, and its usage for "aab" and "baa" examples:
function swap(str, i, j) {
var s = str.split("");
s[i] = str[j];
s[j] = str[i];
return s.join("");
}
function calcMinimumSteps(current, stepsCount)
{
if (typeof(memory[current]) !== "undefined") {
if (memory[current] > stepsCount) {
memory[current] = stepsCount;
} else if (memory[current] < stepsCount) {
stepsCount = memory[current];
}
} else {
memory[current] = stepsCount;
calcMinimumSteps(swap(current, 0, current.length-1), stepsCount+1);
for (var i = 0; i < current.length - 1; ++i) {
calcMinimumSteps(swap(current, i, i + 1), stepsCount+1);
}
}
}
var memory = {};
calcMinimumSteps("aab", 0);
alert("Minimum steps count: " + memory["baa"]);
Here is the ruby logic for this problem, copy this code in to rb file and execute.
str1 = "education" #Sample first string
str2 = "cnatdeiou" #Sample second string
moves_count = 0
no_swap = 0
count = str1.length - 1
def ends_swap(str1,str2)
str2 = swap_strings(str2,str2.length-1,0)
return str2
end
def swap_strings(str2,cp,np)
current_string = str2[cp]
new_string = str2[np]
str2[cp] = new_string
str2[np] = current_string
return str2
end
def consecutive_swap(str,current_position, target_position)
counter=0
diff = current_position > target_position ? -1 : 1
while current_position!=target_position
new_position = current_position + diff
str = swap_strings(str,current_position,new_position)
# p "-------"
# p "CP: #{current_position} NP: #{new_position} TP: #{target_position} String: #{str}"
current_position+=diff
counter+=1
end
return counter,str
end
while(str1 != str2 && count!=0)
counter = 1
if str1[-1]==str2[0]
# p "cross match"
str2 = ends_swap(str1,str2)
else
# p "No match for #{str2}-- Count: #{count}, TC: #{str1[count]}, CP: #{str2.index(str1[count])}"
str = str2[0..count]
cp = str.rindex(str1[count])
tp = count
counter, str2 = consecutive_swap(str2,cp,tp)
count-=1
end
moves_count+=counter
# p "Step: #{moves_count}"
# p str2
end
p "Total moves: #{moves_count}"
Please feel free to suggest any improvements in this code.
Try this code. Hope this will help you.
public class TwoStringIdentical {
static int lcs(String str1, String str2, int m, int n) {
int L[][] = new int[m + 1][n + 1];
int i, j;
for (i = 0; i <= m; i++) {
for (j = 0; j <= n; j++) {
if (i == 0 || j == 0)
L[i][j] = 0;
else if (str1.charAt(i - 1) == str2.charAt(j - 1))
L[i][j] = L[i - 1][j - 1] + 1;
else
L[i][j] = Math.max(L[i - 1][j], L[i][j - 1]);
}
}
return L[m][n];
}
static void printMinTransformation(String str1, String str2) {
int m = str1.length();
int n = str2.length();
int len = lcs(str1, str2, m, n);
System.out.println((m - len)+(n - len));
}
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
String str1 = scan.nextLine();
String str2 = scan.nextLine();
printMinTransformation("asdfg", "sdfg");
}
}
Thinking about this question on testing string rotation, I wondered: Is there was such thing as a circular/cyclic hash function? E.g.
h(abcdef) = h(bcdefa) = h(cdefab) etc
Uses for this include scalable algorithms which can check n strings against each other to see where some are rotations of others.
I suppose the essence of the hash is to extract information which is order-specific but not position-specific. Maybe something that finds a deterministic 'first position', rotates to it and hashes the result?
It all seems plausible, but slightly beyond my grasp at the moment; it must be out there already...
I'd go along with your deterministic "first position" - find the "least" character; if it appears twice, use the next character as the tie breaker (etc). You can then rotate to a "canonical" position, and hash that in a normal way. If the tie breakers run for the entire course of the string, then you've got a string which is a rotation of itself (if you see what I mean) and it doesn't matter which you pick to be "first".
So:
"abcdef" => hash("abcdef")
"defabc" => hash("abcdef")
"abaac" => hash("aacab") (tie-break between aa, ac and ab)
"cabcab" => hash("abcabc") (it doesn't matter which "a" comes first!)
Update: As Jon pointed out, the first approach doesn't handle strings with repetition very well. Problems arise as duplicate pairs of letters are encountered and the resulting XOR is 0. Here is a modification that I believe fixes the the original algorithm. It uses Euclid-Fermat sequences to generate pairwise coprime integers for each additional occurrence of a character in the string. The result is that the XOR for duplicate pairs is non-zero.
I've also cleaned up the algorithm slightly. Note that the array containing the EF sequences only supports characters in the range 0x00 to 0xFF. This was just a cheap way to demonstrate the algorithm. Also, the algorithm still has runtime O(n) where n is the length of the string.
static int Hash(string s)
{
int H = 0;
if (s.Length > 0)
{
//any arbitrary coprime numbers
int a = s.Length, b = s.Length + 1;
//an array of Euclid-Fermat sequences to generate additional coprimes for each duplicate character occurrence
int[] c = new int[0xFF];
for (int i = 1; i < c.Length; i++)
{
c[i] = i + 1;
}
Func<char, int> NextCoprime = (x) => c[x] = (c[x] - x) * c[x] + x;
Func<char, char, int> NextPair = (x, y) => a * NextCoprime(x) * x.GetHashCode() + b * y.GetHashCode();
//for i=0 we need to wrap around to the last character
H = NextPair(s[s.Length - 1], s[0]);
//for i=1...n we use the previous character
for (int i = 1; i < s.Length; i++)
{
H ^= NextPair(s[i - 1], s[i]);
}
}
return H;
}
static void Main(string[] args)
{
Console.WriteLine("{0:X8}", Hash("abcdef"));
Console.WriteLine("{0:X8}", Hash("bcdefa"));
Console.WriteLine("{0:X8}", Hash("cdefab"));
Console.WriteLine("{0:X8}", Hash("cdfeab"));
Console.WriteLine("{0:X8}", Hash("a0a0"));
Console.WriteLine("{0:X8}", Hash("1010"));
Console.WriteLine("{0:X8}", Hash("0abc0def0ghi"));
Console.WriteLine("{0:X8}", Hash("0def0abc0ghi"));
}
The output is now:
7F7D7F7F
7F7D7F7F
7F7D7F7F
7F417F4F
C796C7F0
E090E0F0
A909BB71
A959BB71
First Version (which isn't complete): Use XOR which is commutative (order doesn't matter) and another little trick involving coprimes to combine ordered hashes of pairs of letters in the string. Here is an example in C#:
static int Hash(char[] s)
{
//any arbitrary coprime numbers
const int a = 7, b = 13;
int H = 0;
if (s.Length > 0)
{
//for i=0 we need to wrap around to the last character
H ^= (a * s[s.Length - 1].GetHashCode()) + (b * s[0].GetHashCode());
//for i=1...n we use the previous character
for (int i = 1; i < s.Length; i++)
{
H ^= (a * s[i - 1].GetHashCode()) + (b * s[i].GetHashCode());
}
}
return H;
}
static void Main(string[] args)
{
Console.WriteLine(Hash("abcdef".ToCharArray()));
Console.WriteLine(Hash("bcdefa".ToCharArray()));
Console.WriteLine(Hash("cdefab".ToCharArray()));
Console.WriteLine(Hash("cdfeab".ToCharArray()));
}
The output is:
4587590
4587590
4587590
7077996
You could find a deterministic first position by always starting at the position with the "lowest" (in terms of alphabetical ordering) substring. So in your case, you'd always start at "a". If there were multiple "a"s, you'd have to take two characters into account etc.
I am sure that you could find a function that can generate the same hash regardless of character position in the input, however, how will you ensure that h(abc) != h(efg) for every conceivable input? (Collisions will occur for all hash algorithms, so I mean, how do you minimize this risk.)
You'd need some additional checks even after generating the hash to ensure that the strings contain the same characters.
Here's an implementation using Linq
public string ToCanonicalOrder(string input)
{
char first = input.OrderBy(x => x).First();
string doubledForRotation = input + input;
string canonicalOrder
= (-1)
.GenerateFrom(x => doubledForRotation.IndexOf(first, x + 1))
.Skip(1) // the -1
.TakeWhile(x => x < input.Length)
.Select(x => doubledForRotation.Substring(x, input.Length))
.OrderBy(x => x)
.First();
return canonicalOrder;
}
assuming generic generator extension method:
public static class TExtensions
{
public static IEnumerable<T> GenerateFrom<T>(this T initial, Func<T, T> next)
{
var current = initial;
while (true)
{
yield return current;
current = next(current);
}
}
}
sample usage:
var sequences = new[]
{
"abcdef", "bcdefa", "cdefab",
"defabc", "efabcd", "fabcde",
"abaac", "cabcab"
};
foreach (string sequence in sequences)
{
Console.WriteLine(ToCanonicalOrder(sequence));
}
output:
abcdef
abcdef
abcdef
abcdef
abcdef
abcdef
aacab
abcabc
then call .GetHashCode() on the result if necessary.
sample usage if ToCanonicalOrder() is converted to an extension method:
sequence.ToCanonicalOrder().GetHashCode();
One possibility is to combine the hash functions of all circular shifts of your input into one meta-hash which does not depend on the order of the inputs.
More formally, consider
for(int i=0; i<string.length; i++) {
result^=string.rotatedBy(i).hashCode();
}
Where you could replace the ^= with any other commutative operation.
More examply, consider the input
"abcd"
to get the hash we take
hash("abcd") ^ hash("dabc") ^ hash("cdab") ^ hash("bcda").
As we can see, taking the hash of any of these permutations will only change the order that you are evaluating the XOR, which won't change its value.
I did something like this for a project in college. There were 2 approaches I used to try to optimize a Travelling-Salesman problem. I think if the elements are NOT guaranteed to be unique, the second solution would take a bit more checking, but the first one should work.
If you can represent the string as a matrix of associations so abcdef would look like
a b c d e f
a x
b x
c x
d x
e x
f x
But so would any combination of those associations. It would be trivial to compare those matrices.
Another quicker trick would be to rotate the string so that the "first" letter is first. Then if you have the same starting point, the same strings will be identical.
Here is some Ruby code:
def normalize_string(string)
myarray = string.split(//) # split into an array
index = myarray.index(myarray.min) # find the index of the minimum element
index.times do
myarray.push(myarray.shift) # move stuff from the front to the back
end
return myarray.join
end
p normalize_string('abcdef').eql?normalize_string('defabc') # should return true
Maybe use a rolling hash for each offset (RabinKarp like) and return the minimum hash value? There could be collisions though.