Trouble with finding odd and even index values for characters - java

I'm creating simple encryption/decryption software as a fun project. I am able to encrypt. It gives me the encrypted password and the key. The problem I'm having is in the decryption. For an example, here is an encrypted message: eUwG:Q_vul=u^CMiojpnif and here is the key: 103141109141719141119050117050318040907010912. The way it works is by first adding a random salt to the beginning or end of the message which is defined by the first number in the key. So since the first number is 1 then the salt is at the beginning (the salts are 14 characters long), so it removes that text, leaving Miojpnif. In the key after the salt number, there are 2 numbers per letter in the text. which is where I'm stuck. A number 1 means that the character was shifted forward, and 0 means backwards. So for the 'M' in the key for that character it's 0 so it was shifted backwards and the next number is 3 meaning that it was shifted backwards by 3. So to reverse this I need to shift that character forward 3. The thing is, I can't figure out how to make this work properly. My idea is that it removes the first number (salt) from the key, and then if the number is odd, then it records if the character will go forward or back, and if it's even then it'll move that character forward or back (which is stored as an int like the rest) by that number. So where I'm stuck is that figuring out if it's even or odd isn't working properly and I can't quite figure out how to shift that character.
I already looked up how to figure out if it's even or odd, but it still doesn't work. Actually shifting the character I made up my own code for. I don't know if you guys understand what I need help with because I didn't really know how to express it in words. So here is the code that I have, I hope that y'all can help.
for(int i= 0; i < keyNew.length(); i++){
if(i % 2 == 1){
/*odd*/
if(keyNew.charAt(i) == '1') {
forward = 1;
backward = 0;
} else {
forward = 0;
backward = 1;
}
}else{
/*even*/
if(forward == 1 && backward == 0) {
/*forward*/
System.out.println("forward");
String encryptedNewer = encryptedNew.charAt(i / 2) += keyNew.charAt(i);
} else if(forward == 0 && backward == 1) {
*backward*/
System.out.println("backward");
String encryptedNewer = encryptedNew.charAt(i / 2) += keyNew.charAt(i);
}
}
}
encrypted is the encrypted text, key is the key, encryptedNew is the text without the salt and keyNew is the key without the first digit.

This is a great example of the long method smell. I recommend the following:
Work with String and use the substring(...) function. It is easier, because you need less variables and don't have to convert from char to int and back.
Create a function encrypt(...) and decrypt(...) which calls some "subfunctions"
One subfunction is addSalt(...) and removeSalt(...)
One subfunction is splitKeyToPairs(...) which returns a List of strings with 2 Digits per Item.
One subfunction is shiftForward(...) and shiftBackwards(...)
Then I would implement it as follow:
public String decrypt(String key, String cipher) {
String cipherModified = removeSalt(key, cipher);
List<String> keyPairs = splitKeyToPairs(key.substring(1, key.length()));
String message = "";
for(int position = 0; position < keyPairs.size();++position) {
String keyTmp = keyPairs.get(position);
String cipherLetter = cipherModified.substring(position, position + 1);
message += "0".equals(keyTmp.substring(0, 1)) ? shiftBackwards(cipherLetter, keyTmp.substring(1, 2)) : shiftForward(cipherLetter, keyTmp.substring(1, 2));
}
return message;
}
public List<String> splitKeyToPairs(String key) {
List<String> result = new ArrayList<String>();
for(int i = 0; i < key.length(); i += 2) {
//i += 2 because we jump 2 characters per iteration.
result.add(key.substring(i, i+2));
}
return result;
}
Here a little test function for the split but not for the decrypt:
public static void main(String[] args) {
List<String> result = splitKeyToPairs("1234567890");
List<String> test = new ArrayList<>(Arrays.asList("12", "34", "56", "78", "90"));
for(int i = 0; i < result.size();++i) {
if(!result.get(i).equals(test.get(i))) {
System.out.println("error on position " + i);
}
}
}

Ok, here is another approach to decrypting the message. Establishing methods and tucking them away in a library would be advisable. In the following example I omitted the salt digit from the key. The logic to accommodate that is trivial.
String key = "03141109141719141119050117050318040907010912";
String code = "eUwG:Q_vul=u^CMiojpnif";
int i = 0;
StringBuilder sb = new StringBuilder();
for (int pos = 0; pos < code.length(); pos++) {
// get codeLetter
char codeLetter = code.charAt(pos);
// set direction
int direction = key.charAt(i++) == '0' ? 1
: -1;
// set count
int count = key.charAt(i++) - '0';
// modify codeLetter based on direction and count
char c = (char) (codeLetter + (count * direction));
// save it.
sb.append(c);
}
System.out.println(sb.toString().substring(14));

Related

Java method to check if a given number is symmetric

iv made a method to check if a given number is symmetric or not.
-the method is based on the idea that the given number is an array .
-i gave the methood 4 deifferent parameters- numberLength , middle , point1,point2(both pointing at a certain digit in the number(as an array))
-although wrote the code correctly , and it works when im initializing a specific array ,
i dont know how to use the method for a given number .
How can i set this Method - with parameter (int number) , so i can chek on the given number.
Thank you A lot
update** i added the code :
public boolean isSymmetric(int num){
int digits;
int[] number;
number = new int[num];
int length = number.length;
int mid;
if(length%2 == 0) //
{
mid = length/2;
}else {
mid = length/2+1;
}
int pointer1 =0;
int pointer2 = mid;
while(pointer1<mid && pointer2 < length)
{
if(number[pointer1] == number[pointer2])
{
pointer1=pointer1+1;
pointer2=pointer2+1;
}
else
System.out.println("number is not symmetric");
return false;
}
System.out.println("number is symmetric");
return true;
}
The easiest way to check if number symmetric or not is to map it to String::class, just like this:
// Your input number
Integer maybeSymmetricNumber = 12321;
String str = String.valueOf(maybeSymmetricNumber), reverseStr = "";
int strLength = str.length();
for (int i = (strLength - 1); i >=0; --i) {
reverseStr = reverseStr + str.charAt(i);
}
if (str.toLowerCase().equals(reverseStr.toLowerCase())) {
System.out.println(str + " is a symmetric number.");
}
else {
System.out.println(str + " is not a symmetric number.");
}
First, here is a method to convert your number to an array of ints.
it works by using the Math.log10 to compute the exponent of 10 for that number.
e.g. Math.log10(1234) = 3.xxx. So convert to an int and add 1 to get 4.
e.g. Math.log10(1000) = 3 so do the same thing.
then use that number to create an array of proper size.
and use the remainder(%) and division(/) operators to populate it.
then return the array.
public static int[] toArray(int number) {
number = Math.abs(number);
int[] arr = new int[(int) Math.log10(number)+1];
int i = 0;
while (number > 0) {
arr[i++] = number%10;
number/=10;
}
return arr;
}
Unfortunately, your method doesn't work properly as it always returns false. This is because of missing {} around the following.
else
System.out.println("number is not symmetric");
return false;
But it still doesn't process all values properly. Try 1234321 and 112211. They both return false but are symmetric. This is because pointer1 starts at 0 and pointer2 starts at mid and they are both incremented by 1.
I suggest you start pointer2 at length-1, going from outside in toward mid. This should then solve the issue (with a little debugging of course).

How to remove duplicate letters while preserving the smallest lexicographical order

I have a task to remove duplicates from given string (classic interview question), but this one is a bit different, the end result should be in the smallest lexicographical order possible among other. For example, cbacdcbc => acdb, bcabc => abc. I saw several related problems in SO, but I could not find the answer.
Edit: Here is my code so far (not working properly):
public static String removeDuplicateCharsAlphbetically(String str) {
int len = str.length();
if (len<2) return str;
char[] letters = str.toCharArray();
int[] counts = new int[26];
for (char c : letters) {
counts[c-97]++;
}
StringBuilder sb = new StringBuilder();
for (int i=0;i<len-1;i++) {
if (letters[i]==letters[i+1]) continue;
if (counts[letters[i]-97]==1) {
sb.append(letters[i]);
} else if (counts[letters[i]-97] != 0) {
if (letters[i]<letters[i+1] && counts[letters[i]-97] == 1) {
sb.append(letters[i]);
counts[letters[i]-97]=0;
} else {
counts[letters[i]-97]--;
}
}
}
return sb.toString();
}
EDIT2: I am sorry for not putting link of the question earlier. here is the link:
This can be done in O(StringLenght) time.
String lenght = N;
Time Complexity O(N) , single scan of the string.
Space Complexity O(26)
Solution:
Create an array of Alphabet letters which will store pointer to doubly link list Node .
ListNode* array[26]; // Initialized with NULL value.
Create an empty linkedlist , which will represent the solution string at any point of time, in reverse order.
Scan the string and for each letter , check the letter ,ltr, check array[ltr-'a']
.... a.) if it is NULL , it means , it is first occurence and add it to the linked list . ... b.) If array is pointing to any node listNodeltr , it means letter is already in result
i. check value for previous listNode to listNodeltr, in linklist ,
If value of listNodeltr->prev->val < listNode->val , it means removing the current node from this position will make the result lexographically smaller .
So remove listNodeLtr from the current postion in linkedList and add it to the end.
Else, current postion of ltr is find and continue.
cbacdcbc
[a]->[b]->[c]
cbacdcbc
[c]->[a]->[b]
cbacdcbc
[d]->[c]->[a]->[b]
cbacdcbc
[b]->[d]->[c]->[a]
cbacdcbc
[b]->[d]->[c]->[a]
print linklist in reverse order : acdb.
First, let's create a set of all distinct letters of the string s. The size of this set is the length of the answer and the number of steps in our algorithm.
We will add one letter to the answer on each step with the following greedy approach:
On every step iterate through the remaining letters in alphabetical order and for every letter l:
Find the first occurrence of l in s. Let's name it lpos.
If the substring s[lpos, end] contains all remaining letters then add l to the result, replace s with s[lpos+1, end] and go to the next step with reduced remaining letters set.
Implementation with some optimizations to achieve better time complexity:
public String removeDuplicateLetters(String s) {
StringBuilder result = new StringBuilder();
int[] subsets = new int[s.length()];
int subset = 0;
for (int i = s.length() - 1; i >= 0; i--) {
char ch = s.charAt(i);
subset = addToSet(subset, ch);
subsets[i] = subset;
}
int curPos = 0;
while (subset != 0) {
for (char ch = 'a'; ch <= 'z'; ++ch) {
if (inSet(subset, ch)) {
int chPos = s.indexOf(ch, curPos);
if (includes(subsets[chPos], subset)) {
result.append(ch);
subset = removeFromSet(subset, ch);
curPos = chPos + 1;
break;
}
}
}
}
return result.toString();
}
private boolean inSet(int set, char ch) {
return (set & (1 << (ch - 'a'))) != 0;
}
private boolean includes(int set, int subset) {
return (set | subset) == set;
}
private int addToSet(int set, char ch) {
return set | (1 << (ch - 'a'));
}
private int removeFromSet(int set, char ch) {
return set & ~(1 << (ch - 'a'));
}
Runnable version: https://ideone.com/wIKi3x
Observation 1: the first letter of the output is the least letter such that all other letters appear to the right of its leftmost appearance in the string.
Observation 2: the remaining letters of the output are a subsequence of the letters to the right of the leftmost appearance of the first letter.
This suggests a recursive algorithm.
def rem_dups_lex_least(s):
if not s:
return ''
n = len(set(s)) # number of distinct letters in s
seen = set() # number of distinct letters seen while scanning right to left
for j in range(len(s) - 1, -1, -1): # len(s)-1 down to 0
seen.add(s[j])
if len(seen) == n: # all letters seen
first = min(s[:j+1])
i = s.index(first) # leftmost appearance
return first + rem_dups_lex_least(''.join(c for c in s[i+1:] if c != first))
Build the result by going backwards from end of the input to start. On each step:
If new letter is encountered, prepend it to result.
If duplicate is encountered, then compare it with the head of result. If head is greater, remove duplicate from the result and prepend it instead.
LinkedHashSet is good both for storing result set and its internal order.
public static String unduplicate(String input) {
Character head = null;
Set<Character> set = new LinkedHashSet<>();
for (int i = input.length() - 1; i >= 0; --i) {
Character c = input.charAt(i);
if (set.add(c))
head = c;
else if (c.compareTo(head) < 0) {
set.remove(c);
set.add(head = c);
}
}
StringBuilder result = new StringBuilder(set.size());
for (Character c: set)
result.append(c);
return result.reverse().toString();
}

Checksums - ISBN program

This problem has me puzzled. I tried using a loop like this: Basically I tried to get the first digit from the input and do the formula but it doesn't seem to work. It looks so simple but I can't figure it out. Could you help me? Thanks.
public static int ISBN(String ninedigitNum) {
number = 9;
while (number > 0) {
int nextDigit = ninedigitNum.substring(0,1);
...
}
Checksums (Source: Princeton University). The International Standard
Book Number (ISBN) is a 10 digit code that uniquely specifies a book.
The rightmost digit is a checksum digit which can be uniquely
determined from the other 9 digits from the condition that d1 + 2d2 +
3d3 + ... + 10d10 must be a multiple of 11 (here di denotes the ith
digit from the right). The checksum digit d1 can be any value from 0
to 10: the ISBN convention is to use the value X to denote 10.
Example: the checksum digit corresponding to 020131452 is 5 since is
the only value of d1 between 0 and and 10 for which d1 + 2*2 + 3*5 +
4*4 + 5*1 + 6*3 + 7*1 + 8*0 + 9*2 + 10*0 is a multiple of 11. Create a
Java method ISBN() that takes a 9-digit integer as input, computes the
checksum, and returns the 10-digit ISBN number. Create 3 JUnit test
cases to test your method.
I got it, thanks a lot everyone!
What about it isn't working? Either way, I believe what you're missing is that you're continually getting the same substring, which will be the first number of the string: int nextDigit = ninedigitNum.substring(0,1);. In addition, you're going to want to use an int, not a String; you can technically convert from String to int if desired, but the problem itself calls for an int.
There are two ways to do this that jump to mind. I would do this by realizing that mod in powers of 10 will give you the respective digit of an integer, but the easier way is to convert to a char array and then access directly. Note that there's no error checking here; you'll have to add that yourself. In addition, there are a LOT of 'magic numbers' here: good code typically has very, very few. I would recommend learning more data structures before attempting problems like these; to be honest there's very few things you can do without at least arrays and linked lists.
char[] ISBN = ninedigitNum.toCharArray();
//Process each number
int total = 0;
for(int i=0; i<9; i++){
int current_int = Integer.parseInt(ISBN[i]);
total += current_int * (10 - i)
}
//Find value of d1
for(int i=0; i<9; i++){
if(((total + i) % 11) == 0){
total += i*100000000;
break;
}
}
return total;
In general: Use print outs with System.out.println(x); or use your compiler's debugger to see what's going on during processing.
So,
This is the piece of code that I wrote. I still think it could be made more efficient.
public class Problem3 {
public static String ISBN(String x)
{
char[]temp = x.toCharArray();
int counter = 2;
int sum = 0;
int j=0;
for(int i=8;i>=0;i--)
{
sum+= counter*Integer.parseInt(""+temp[i]);
counter+=1;
}
for(j=0;j<10;j++)
{
if((sum+j)%11==0)
{
break;
}
}
return x+""+j;
}
public static void main(String args[])
{
String a = "020131452";
System.out.println(ISBN(a));
}
}
Hope this helps.
This works:
public static int ISBN(String nineDigitNum){
int sum = 0;
for(int i = 0; i<nineDigitNum.length(); i++){
sum += Integer.parseInt(""+nineDigitNum.charAt(i))*(10-i);
}
return (sum%11);
}
Also I believe if the checksum is == to 10, it should return an X, so you could either change the return type and add an if statement somewhere, or just put the if statement outside wherever you are using this method.
Here is a short one without loops that uses only substring(), charAt() and length():
public static String ISBN(String nineDigits) {
int chkD = 11 - checkDigit(nineDigits, 0);
return nineDigits + ((chkD == 10) ? "X" : chkD);
}
public static int checkDigit(String nDsub, int chkD) {
if (nDsub.length() == 0)
return 0;
chkD = ((nDsub.charAt(0) - '0') * (nDsub.length() + 1));
return (chkD + checkDigit(nDsub.substring(1), chkD)) % 11;
}
Output:
> ISBN("123456789")
"123456789X"
> ISBN("123456780")
"1234567806"

some weird stff im running into on java

So im working on java codingbat and this is the question:
Given a string, look for a mirror image (backwards) string at both the beginning and end of the given string.
In other words, zero or more characters at the very begining of the given string, and at the very end of the string in reverse order (possibly overlapping).
For example:
the string "abXYZba" has the mirror end "ab". mirrorEnds("abXYZba") → "ab" mirrorEnds("abca") → "a" mirrorEnds("aba") → "aba" .
My code passed all the test except for the other test, which is not specified. I dont know what's wrong with it.
public String mirrorEnds(String string) {
String input = string, mirror = "";
int length = string.length();
for (int n = 0; n < (length+1) / 2; n++) {
if (input.charAt(n) != input.charAt(length - n - 1)) {
break;
}else if(length%2 == 1 && n == (length - 1)/2){
// System.out.println("length/2 = " );
return input;
}
else {
mirror += input.charAt(n);
}
}
return mirror;
}
You were correct in not needing to go though the entire word, but your logic is more complex than it needs to be, making it harder to find and fix the problem. The root cause of the test failure is in the last return statement. It must return string if the loop completes without breaking. You can fix your code by changing break; to return mirror; and changing the last return mirror; to return input;
The test that is failing is one like this:
mirrorEnds("abba") -> "abba"
A much simpler version of your code can be created like this:
public String mirrorEnds(String string) {
int len = string.length();
for (int i=0; i < len/2; ++i)
if (string.charAt(i) != string.charAt(len - 1 - i))
return string.substring(0, i);
return string;
}
mirrorEnds("abba")?
Anyways, I'm sure you could come up with a better question name than "some weird stuff"...
Since you are dividing n by 2 in your loop termination condition, it will end when halfway through the word. This is enough to tell the word is a palindrome, but not enough to build your output correctly. You have a condition handling palindrome with odd numbers of letter, but not even numbers of letters. I believe the failing test will be of the form "abba", where I believe what you have will return "ab", instead of "abba".
If you change you loop to:
for (int n = 0; n < length; n++) {
I believe it should be doing what you want. This also makes the short circuit case unnecessary, so:
for (int n = 0; n < length; n++) {
if (input.charAt(n) != input.charAt(length - n - 1)) {
break;
}
else {
mirror += input.charAt(n);
}
}
The first test I tried was with the string "abba" which fails. It returns ab, and not abba. As femtoRgon mentioned, you're not going through the entire word, which may some times be necessary. femtoRgon's solution works, as well as taking a slightly different approach to iterating through the word as follows:
public String mirrorEnds(String string) {
boolean matches = true;
StringBuilder mirrorEnd = new StringBuilder();
int index = 0;
while (matches && index < string.length()) {
if (string.charAt(index) == string.charAt(string.length() - index - 1))
mirrorEnd.append(string.charAt(index));
else
matches = false;
index++;
}
return mirrorEnd.toString();
}
public String mirrorEnds(String string) {
String comp="";
for(int i=0; i<string.length(); i++){
if(string.charAt(i)==string.charAt(string.length()-(i+1)))
comp= comp+ string.charAt(i);
else break;
}
return comp;
}

Array How would you add a digit to the same location as its value?

The input comes as a String "543210".
The code extracts each character using the charAt method and place them one after the other in a specific array location that corresponds to the value of the number.
charAt(0) = 5 means that 5 should go intoarrayLocation 5.
It doesnt seem to work. I even tried with arrayLists.
public class HugeInteger {
private String digits;
int[] arrayToStoreTheDigits = new int[6];
public HugeInteger(String digits) {
this.digits = digits;
add();
}
public void add() {
for (int i = 0; i < 5; i++) {
arrayToStoreTheDigits[digits.charAt(i)] = digits.charAt(i);
System.out.println(digits.charAt(i));
}
}
public String toString() {
return "" + arrayToStoreTheDigits + "/ " + digits.charAt(2);
}
}
package Exercise8_17_HugeIntegers;
public class HugeIntegertester {
// static HugeInteger huge;
public static void main(String[] args) {
HugeInteger huge = new HugeInteger("543210");
System.out.println(huge.toString());
}
}
Your question is unclear, but I suspect the problem is here:
arrayToStoreTheDigits[digits.charAt(i)] = digits.charAt(i);
If digits.charAt(i) is '5' that has an integer value of 53, as that's the UTF-16 code unit for the character '5'. If you're trying to extract its value when viewed as a digit, you need to use Character.digit. Alternatively you could just subtract '0' if you really only care about 0-9, and are confident there will be no other characters.
So you could write your code like this:
char c = digits.charAt(i);
arrayToStoreTheDigits[c - '0'] = c;
Note that due to this initialization:
int[] arrayToStoreTheDigits = new int[6];
... your code will fail if it ever sees a value of '6' or greater.
Additionally, if you want to use all the characters in digits, your loop should be:
for (int i = 0; i < digits.length(); i++)
Overall this is a very odd thing to want to do - because the only values valid for array element 1 (for example) will be '1' (if the digit is present) or 0 (the default, if it's not). In particular, this loses all information about the position in which the digits occurred. If the class is meant to be similar to BigInteger, you should be writing something much more like this:
arrayToStoreTheDigits = new int[digits.length()];
for (int i = 0; i < arrayToStoreTheDigits.length; i++)
{
// TODO: Digit validation
arrayToStoreTheDigits[i] = digits.charAt(i) - '0';
}
So that after passing in "543210" you'd have an array of { 5, 4, 3, 2, 1, 0 }. That's now useful information.
Problem exists with your loop :
for (int i = 0; i < 5; i++) { // condition shoule be i < 6
// arrayToStoreTheDigits[digits.charAt(i)] = digits.charAt(i); // convert String to integer
// shoule be
arrayToStoreTheDigits[Integer.parseInt(digits.charAt(i))] = Integer.parseInt(digits.charAt(i));
System.out.println(digits.charAt(i));
}

Categories

Resources