What is the time complexity of lastIndexOf in java? - java

What is the time complexity of the lastIndexOf in java for using it with Strings.
Suppose: int t= s.lastIndexOf(c);
where s is some String and c is s.charAt(0)

Look at the implementation of this method. In both code branches it contain simple iteration over string's internal array:
int i = Math.min(fromIndex, value.length - 1);
for (; i >= 0; i--) {
if (value[i] == ch) {
return i;
}
}
Hence complexity is just O(n), i.e. linear.

For String.lastIndexOf(int) it's linear - O(n).
You at least need to iterate over the whole character sequence, end to start.
And that's what Java does.
Worst case: "abbbbbbbbbb".lastIndexOf('a')
For String.lastIndexOf(String) it's O(n*m) where n is input length and m is parameter length.
In this case, Java iterates end to start and wherever it finds matching last character of the needle it tries to match preceding characters on both sides as well.

As soon as String is implemented as char array it is O(n).
public int lastIndexOf(int ch, int fromIndex) {
if (ch < Character.MIN_SUPPLEMENTARY_CODE_POINT) {
// handle most cases here (ch is a BMP code point or a
// negative value (invalid code point))
final char[] value = this.value;
int i = Math.min(fromIndex, value.length - 1);
for (; i >= 0; i--) {
if (value[i] == ch) {
return i;
}
}
return -1;
} else {
return lastIndexOfSupplementary(ch, fromIndex);
}
}

Related

First Unique Character In a String-Leetcode

I have tried out 387.First Unique Character In A string
Given a string s, find the first non-repeating character in it and
return its index. If it does not exist, return -1.
EXAMPLE : 1
Input: s = "leetcode"
Output: 0
EXAMPLE :2
Input: s = "loveleetcode"
Output: 2
I have been trying this problem. I thought we will pick one by one all the characters and check if a repeating character exists break from the loop. And if not then return that index.I have thought over a solution which I believe is not the most efficient way but I want to know the how can I solve this problem with the approach given below:
public int firstUniqChar(String s) {
for(int i=0;i<s.length();i++){
for(int j=i+1;j<s.length();j++){
if(s.charAt(i)==s.charAt(j)){
break;
}
}
}
return -1;
}
I'm confused how to return the index.I'm unable to find the logic after:
for(int j=i+1;j<s.length();j++){
if(s.charAt(i)==s.charAt(j)){
break;
}
}
If anyone can help me find out the logic here.
Try this.
public static int firstUniqChar(String s) {
L: for (int i = 0, length = s.length(); i < length; i++) {
for (int j = 0; j < length; j++)
if (i != j && s.charAt(i) == s.charAt(j))
continue L;
return i;
}
return -1;
}
public static void main(String[] args) {
System.out.println(firstUniqChar("leetcode"));
System.out.println(firstUniqChar("loveleetcode"));
System.out.println(firstUniqChar("aabb"));
}
output:
0
2
-1
you can use a flag variable.
public int firstUniqChar(String s) {
int flag=0;
for(int i=0;i<s.length();i++){
flag=0;
for(int j=0;j<s.length();j++){
if(s.charAt(i)==s.charAt(j) && i!=j){
flag=1;
break;
}
}
if(flag==0){
return i;
}
}
return -1;
}
There are 26 possible lowercase English letters, so you could use two 26 element arrays.
One array, letterCount, keeps counts of each letter. Start at 0 and add 1 every time the corresponding letter appears in the text string. The second array, position, holds the position of the first occurrence of that letter, or -1 if the letter never appears. You will need to initialise that array to -1 for all elements.
Process the string in order, recording initial positions, once only for each letter, and incrementing the count for each letter in the string.
After the string has been processed, look through the letterCount array. If there are no letters with a 1 count then return -1. If exactly one letter has a 1 count, then return the position of that letter from the position array. If more than one letter has a 1 count, then pick the one with the lowest value for its position.
Using two loops is a highly inefficient way of solving this problem. The string can be up to 100,000 characters long and you are processing it multiple times. Far better to process it only once, keeping track of what you have found so far.
Fix you code
You need to add a variable that tells you if you have breaked the loop or not
static int firstUniqChar(String s) {
boolean duplicate;
for (int i = 0; i < s.length(); i++) {
duplicate = false;
for (int j = i + 1; j < s.length(); j++) {
if (s.charAt(i) == s.charAt(j)) {
duplicate = true;
break;
}
}
if (!duplicate) {
return i;
}
}
return -1;
}
Improve
There is a smarter way, that is finding the last index occurence of the current char, if it's equal to the current index : that char is unique and you return its index
static int firstUniqChar(String s) {
for (int i = 0; i < s.length(); i++) {
if (s.lastIndexOf(s.charAt(i)) == i) {
return i;
}
}
return -1;
}
If you do not bother about time complexity using IdenxOF operations, then one can try this solution.
indexOf() – also runs in linear time. It iterates through the internal array and checking each element one by one. So the time
complexity for this operation always requires O(n) time.
int firstUniqCharOneLoop(String str) {
for (int i = 0; i < str.length(); i++) {
if (str.indexOf(str.charAt(i))== str.lastIndexOf(str.charAt(i)) ) {
return i;
}
}
return -1;
}
The lowest complexity I managed to achieve:
public class UniqueSymbolFinder {
static int findFirstUniqueChar(String s) {
Set<Character> set = new HashSet<>(s.length());
List<CharWithIndex> candidates = new LinkedList<>();
for (int i = 0; i < s.length(); i++) {
char ch = s.charAt(i);
CharWithIndex charWithIndex = new CharWithIndex(ch, i);
if (set.add(ch)) {
candidates.add(charWithIndex);
} else {
candidates.remove(charWithIndex);
}
}
return candidates.size() == 0 ? -1 : candidates.get(0).index;
}
/**
* Class for storing the index.
* Used to avoid of using an indexOf or other iterations.
*/
private static class CharWithIndex {
int index;
char ch;
private CharWithIndex(char ch, int index) {
this.ch = ch;
this.index = index;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
CharWithIndex that = (CharWithIndex) o;
return ch == that.ch;
}
#Override
public int hashCode() {
return Objects.hash(ch);
}
}
}
I believe the memory usage can still be optimized.
100% Correct JAVA Solution
Since the question is about returning the index of the first non-repeating character in a string, we need some data structure to save the index of each character in the string for us.
I choose here the HashMap of Java. Basically, what you can do with it, you can save a pair of values (or pair of other data structures).
So, in my solution, I am saving a Character Integer pair. The first is considered as a key (here it is each character in the string), and the second is its index value.
The problem here is that we only want to keep the minimum index of non-repeating characters and that's why if you take a look below, you will find the maxIndexForRepeatedValues is set to be 10 power 5 as the input constraint says 1 <= s.length <= 10 power 5.
However, I am using that value to neglect repeated characters that would be found in the HashMap and at the end, we retrieve the minimum index which is the index of course for the first character from the map, or if there were only repeated characters, we return -1.
To make the code shorter, I used ternary-operator but you can write it with if-else if you want!
class Solution {
public int firstUniqChar(String s) {
int maxIndexForRepeatedValues = 100000;
Map<Character, Integer> map = new HashMap<>();
for (int i = 0 ; i < s.length() ; i++) {
char key = s.charAt(i);
int resIndex = map.containsKey(key) ? maxIndexForRepeatedValues : i;
map.put(key, resIndex);
}
int minIndex = Collections.min(map.values());
return minIndex == maxIndexForRepeatedValues ? -1 : minIndex;
}
}

Implement String.length method by hand

I have to implement the .length method from String class "by hand" and I have no idea and hope you can help somehow.
No other methods or functions are allowed, than:
String.charAt()
String.substring()
String.isEmpty()
Bit-operations &,|, &&,||, <<, >>,>>>, !=, ==
Arithmetic operations
for and while Loop
recursion
if else statement
self created methods (int,String,char,boolean etc.)
self-created Arrays. (no Methods of them)
static void manual_length2(String length) {
//example String length = "Hello" = 5 letters.
int counter = 0;
int i = 0;
char g = ' ';
while(i <= 4 ) { /*4 is the number i already know */
g = length.charAt(i);
counter += 1;
length.substring(1);
++i;
}
System.out.println(counter);
Console: 5
This was my approach, but I'm stuck in the while statement's condition to terminate.
With the example "Hello" i already know that this word has 5 letters, but it needs to fit for all inputs. So i don't know how to express to border-value of the while statement.
Another approach is by recursion, but also, i ask myself how can i express the limit of the recursion.
How can i express:
.... lengthMethod1(String length, int ???) {
if(n == 0) {
return length.charAt(0);
}
else {
return ???? lengthMethod1(length, n - 1);
}
You can loop until the String is empty while removing the first character on each iteration.
static int manual_length(String str) {
int len = 0;
while(!str.isEmpty()){
++len;
str = str.substring(1);
}
return len;
}
This can be converted to a tail-recursive method as well.
static int manual_length(String str) {
return str.isEmpty() ? 0 : 1 + manual_length(str.substring(1));
}
Another approach is by recursion, but also, i ask myself how can i
express the limit of the recursion. How can i express:
Yes, you can do recursively like this:
static int manual_length(String str, int len) {
return str.isEmpty() ? len : manual_length(str.substring(1), len + 1);
}
You use an accumulator variable (i.e., len), that you increment, while removing a char from the string (i.e., str.substring(1)). When you reach the end (i.e., str.isEmpty()) you return the accumulator.

Return the count how many digits are 7 in the given number n

I'm writing a method for my CS151 class called countSevens(n). It Returns count how many digits are 7 in the given number n. This is what I have so far but I'm doing something wrong that I can't figure out.
public int countSevens(int n){
int count = 0;
String strI = Integer.toString(n);
for (int i = 0; i < strI.length(); i++){
if(strI.substring(i).equals("7")){
count++;
}
}
return count;
}
You can do it with java streams
public int countSevens(int n) {
return (int) String.valueOf(n).chars().filter(ch -> ch == '7').count();
}
(int) - cast to an int type, in this particular case it safe to cast long to int, because we can't get a conversation error. In other cases it's better to use Math.toIntExact(long)
String.valueOf(n) - convert to string
chars() - return stream of chars
filter(ch -> ch == '7') - filter all chars that equals to 7
count() - returns the count of elements in this stream
strI.substring(i)
Will return the part of string from i-character to the end.
Use strI.charAt(i) instead
From the definition of String.substring(int):
Returns a string that is a substring of this string. The substring begins with the character at the specified index and extends to the end of this string.
So this will only count the last instance of a 7 in your number, and only if it's the last digit in the number.
Instead, try this:
if(strI.substring(i, i+1).equals("7"))
Or, since you're dealing with ints, you can avoid using strings altogether. n % 10 will get you the last digit, and n /= 10 will bump the entire number right by one digit. That should be enough to get you started on doing this without Strings.
To count the number of 7s in an integer:
int counter = 0;
int number = 237123;
String str_number = String.valueOf(number);
for(char c : str_number.toCharArray()){
if(c == '7'){
counter++;
}
}
You can just use simple arithmetics:
public static int countSevens(int i) {
int count = 0;
for (i = i < 0 ? -i : i; i != 0; count += i % 10 == 7 ? 1 : 0, i /= 10);
return count;
}
But who can read this? Not many, so here is a cleaner solution, applying the same logic:
public static int countSevens(int i) {
int count = 0;
// ignore negative numbers
i = Math.abs(i);
while(i != 0) {
// if last digit is a 7
if(i % 10 == 7) {
// then increase the counter
count++;
}
// remove the last digit
i /= 10;
}
return count;
}

Improving Program Efficiency

public class cowcode {
public static void main(String[] args) {
long index = 1000000
String line = HELLO
boolean found = false;
if (index <= line.length())
found = true;
while (!found) {
line += buildString(line);
if (index <= line.length())
found = true;
}
if (found)
System.out.println("" + charAt(line, index-1));
}
public static String buildString(String str){
String temp = "" + str.charAt(str.length()-1);
for (int i = 0; i < str.length()-1; i ++){
temp += str.charAt(i);
}
return temp;
}
public static String charAt(String line, long index){
for (int i = 0; i < line.length(); i ++){
if (i == index)
return line.charAt(i) + "";
}
return "";
}
}
Hey! The code above works perfectly fine. However the only problem is runtime.
The objective of this program is to build a string from "HELLO" (which will eventually have the length of at least size index). This is done by rotating the String to the right ("HELLO" --> "HELLOOHELL", and concatenating the original String and the rotated version together. This process will not stop until the index that the program is looking for is found in the String. (so in this example, the String will become "HELLOOHELLLHELLOOHEL" after going through the loop twice).
Do you guys see anything that could be eliminated/shortened to improve runtime?
What I guess is killing you is all of the String concatenations you're doing in buildString. You can cut it down to this:
public static String buildString(String str){
return str.charAt(str.length()-1) + str.substring(0, str.length()-1);
}
You need to calculate the index without actually building the string. The right half of the composite string it rotated, the left one is not. If You have index in the left half of the string, You can just throw away the right half. Hence You simplified the situation. If You have index in the right half, You can transform it to index in the left half. You just need to undo rotation of the string in the right half. So You rotate the index left by one character. Now You can substract legth of half of the string and You have index in the left half of the string. This situation is already described above. So You shorten the string and start again at the beginning. In the end You end up with the string, that is not composed. It is the original string. Now You can address the characters directly with the index as it is now in range of the string.
index = 1000000 - 1;
line = "HELLO";
int len = line.length();
long len2 = len;
while (len2 <= index) {
len2 *= 2;
}
while (len2 > len) {
long lenhalf = len2 / 2;
if (index >= lenhalf) {
index -= lenhalf;
index -= 1;
if (index < 0) {
index += lenhalf;
}
}
len2 = lenhalf;
}
System.out.println(line.charAt((int)index));

count uppercase chars in string recursively

I have to solve an exercise, counting all the uppercase chars in a String - recursively - Anyhow I thought I might have found a solution - but it won't work…
Probably you might help me? Thanks!
public static int CountCapitals(String s) {
int counter = 0;
// if (Character.isUpperCase(s.charAt(0)))counter+=1;
if (s.length() == 0)
return counter;
if (s.length() == 1 && s.charAt(0) < 65 && s.charAt(0) > 90)
return 0;
if (s.charAt(0) < 'A' && s.charAt(0) > 'Z') {
return CountCapitals(s.substring(1));
}
if (s.charAt(0) >= 'A' && s.charAt(0) <= 'Z')
counter++;
return CountCapitals(s.substring(1));
}
The problem with your code is the use of counter: each level of invocation has its own counter, initially set to zero. The ++ operator at the bottom has no effect.
You need to compute the result of this invocation based on the result of the previous invocation. Your base case (i.e. s.length() == 0) is fine; the rest of your code needs to change so that it returns whatever CountCapitals(s.substring(1)) when the first letter is non-capital; when the first letter is capital, your function should return 1 + CountCapitals(s.substring(1)).
You need to consider the case when the length of string is 1 and the only character is uppercase (in this case, you should return 1).
Also you need to pass in the counter as a parameter rather than expecting it to "carry over" into other function calls.
This recursion should do just what you want:
public static int countCapitals(String s) {
if (s.length() == 0) return 0;
int cap = Character.isUpperCase(s.charAt(0)) ? 1 : 0;
return countCapitals(s.substring(1)) + cap;
}
If this wasn't a home assignment, you could try an iterative approach which is about 5-10 times faster:
public static int countCapitals(String s) {
int count = 0;
for (int idx = 0; idx < s.length(); idx++) {
if (Character.isUpperCase(s.charAt(idx))) {
count++;
}
}
return count;
}
You don't really need to use a counter variable to keep track of the number of capitals. Instead, you can just the recursive calls, themselves, to keep track of the total:
public static int CountCapitals(String s)
{
if (s.length() == 1)
return (Character.isUpperCase(s.charAt(0)) ? 1 : 0);
else
return CountCapitals(s.substring(1)) +
(Character.isUpperCase(s.charAt(0)) ? 1 : 0);
}
If this is for an assignment and you have to use ASCII values, then fine, but if not, you really should just Character.isUpperCase(char c). In case you're not familiar with the conditional operator, it's defined as follows:
if(someExpression == true)
{
//output 1
}
else
{
//output 0
}
is represented succinctly as:
(someExpression == true) ? 1 : 0
NB:
In your example, counter is set to 0 at the beginning of each method call, so that's why it's not working. If you really want to use a counter, pass it as a parameter to the method instead, and update the parameter with each method call. When you get to the end of the String, simply return the parameter.
You try this
public class HelloWorld{
public static int isUpperCase(String str){
if(str.length()==0) return 0;
boolean check =Character.isUpperCase(str.charAt(0));
if(check){
return isUpperCase(str.substring(1))+1;
}
return isUpperCase(str.substring(1));
}
public static void main(String []args){
String n= "FSAsdsadASdcCa";
System.out.println(isUpperCase("FSAsdsadASdcCa"));
}
}

Categories

Resources