Reputation:
Given a string of digits, count the number of subwords (consistent subsequences) that are anagrams of any palindrome.
My attempt in Python:
def ispalin(s):
if len(s)%2==0:
for i in s:
if s.count(i)%2!=0:
return False
else:
sum =0
for i in set(s):
if s.count(i)%2==1:
sum = sum+1
if sum == 1:
return True
else:
return False
return True
def solution(S):
# write your code in Python 3.6
count=len(S)
for i in range(len(S)):
for j in range(i+1,len(S)):
if ispalin(S[i:j+1]):
count=count+1
return count
i/o format
For example, given:
S = "02002"
the function should return 11.
these are 11 substrings whose anagrams are palindrome
"0", "2", "0", "0", "2", "00", "020", "200", "002", "2002", "02002"
It is giving time limit exceeded for big strings. How can I optimize the above code?
i bet there exists a better solution than this here is the proof [image][1]
https://i.sstatic.net/7x3Jq.png
Upvotes: 0
Views: 2276
Reputation: 1038
There is an O(n) solution for this problem. The first thing to notice is, a substring is anagram of any palindrome if number of its including digits be even or at most one odd exist. e.g. "20020" is anagram of plaindrome because number of '2's is even and number of '0's is odd(at most one odd) while "200202" is not ok.
So the only thing we need to keep is parity of number of digits not sum of them. we can use a 10-bit number to show the parities of all digits. Starting from 0 each time we visit a digit in string, we can xor the parity number with (2^digit). following your example for "02002" here is the parity numbers generated by iterating through the string in binary format:
parity_array = {0000000000, 0000000001, 0000000101, 0000000100, 0000000101 0000000001}
Now we need to count the number of anagrams in linear time. Iterating over the parity_array we use another array of size 1024 (let's call it memo) to keep the number of times we visit a specific number in parity_array. As I mentioned before the substring is ok if and only if the number of 1 bits in their binary parity representation be at most 1. So for each member of parity_array we need to check and add 11 elements in memo having xor with current parity_array value equal to: {0 or 1 or 2 or 4 or 8 ... or 1024} and sum up the results. The total complexity is O(n).
Edit: I added C++ code for what I explained above. I can also add python code, if you want:
string sample = "02002";
int parity = 0;
vector<int> parity_array;
parity_array.push_back(parity);
for(int i=0; i<sample.size(); ++i){
parity ^= 1<<(sample[i]-'0');
parity_array.push_back(parity);
}
int memo[1025] = {0};
int res=0;
for(int i=0;i<parity_array.size();++i){
for(int j=-1;j<10;++j)
res += memo[(1<<j)^parity_array[i]];
memo[parity_array[i]]++;
}
cout<<res<<endl;
Upvotes: 2
Reputation: 80187
You can get quadratic complexity using O(n) of memory.
Make array[0..9] of digit counters (or use another data structure convenient for Python)
Walk through the string, increment counter for current digit and add modified array to the list. After that list will contain "cumulative sums" - counts for every digit upto every index (for example, five 2's before 40-th string entry)
Now to get count for digit between i-th and j-th entries, just subtract C[j][digit] - C[i][digit]
Interesting question - does better complexity solution exist?
Upvotes: 0