Dynamic Programming: Is second string subsequence of first JavaScript

We are given two strings str1 and str2, we are required to write a function that checks if str1 is a subsequence of str2.

A subsequence of a string is a new string which is formed from the original string by deleting some (can be none) of the characters without disturbing the relative positions of the remaining characters.

For example, "ace" is a subsequence of "abcde" while "aec" is not because the relative order is not maintained.

Understanding Subsequences

In a subsequence, characters must appear in the same order as in the original string, but they don't need to be consecutive. Here are some examples:

// Valid subsequences of "abcde"
console.log("Valid subsequences:");
console.log("'ace' from 'abcde' - positions 0,2,4");
console.log("'bd' from 'abcde' - positions 1,3"); 
console.log("'e' from 'abcde' - position 4");

// Invalid subsequences
console.log("\nInvalid subsequences:");
console.log("'aec' from 'abcde' - wrong order (e comes before c)");
Valid subsequences:
'ace' from 'abcde' - positions 0,2,4
'bd' from 'abcde' - positions 1,3
'e' from 'abcde' - position 4

Invalid subsequences:
'aec' from 'abcde' - wrong order (e comes before c)

Two-Pointer Approach

We use two pointers to traverse both strings. We advance the first pointer only when characters match, but always advance the second pointer.

const str1 = 'ace';
const str2 = 'abcde';

const isSubsequence = (str1, str2) => {
    let i = 0; // pointer for str1
    let j = 0; // pointer for str2
    
    while (i < str1.length && j < str2.length) {
        if (str1[i] === str2[j]) {
            i++; // move to next character in str1
        }
        j++; // always move to next character in str2
    }
    
    // If we've matched all characters of str1
    return i === str1.length;
};

console.log(`Is "${str1}" a subsequence of "${str2}"?`, isSubsequence(str1, str2));
Is "ace" a subsequence of "abcde"? true

Step-by-Step Example

const isSubsequenceWithSteps = (str1, str2) => {
    let i = 0, j = 0;
    
    console.log(`Checking if "${str1}" is subsequence of "${str2}"`);
    
    while (i < str1.length && j < str2.length) {
        console.log(`Comparing str1[${i}]='${str1[i]}' with str2[${j}]='${str2[j]}'`);
        
        if (str1[i] === str2[j]) {
            console.log(`Match found! Moving both pointers`);
            i++;
        } else {
            console.log(`No match, moving j pointer only`);
        }
        j++;
    }
    
    const result = i === str1.length;
    console.log(`Result: ${result} (matched ${i}/${str1.length} characters)`);
    return result;
};

isSubsequenceWithSteps('ace', 'abcde');
Checking if "ace" is subsequence of "abcde"
Comparing str1[0]='a' with str2[0]='a'
Match found! Moving both pointers
Comparing str1[1]='c' with str2[1]='b'
No match, moving j pointer only
Comparing str1[1]='c' with str2[2]='c'
Match found! Moving both pointers
Comparing str1[2]='e' with str2[3]='d'
No match, moving j pointer only
Comparing str1[2]='e' with str2[4]='e'
Match found! Moving both pointers
Result: true (matched 3/3 characters)

Testing Multiple Cases

const testCases = [
    ['ace', 'abcde', true],
    ['aec', 'abcde', false],
    ['abc', 'aabbcc', true],
    ['axc', 'ahbgdc', false],
    ['', 'abc', true], // empty string is subsequence of any string
    ['abc', '', false] // non-empty cannot be subsequence of empty
];

testCases.forEach(([str1, str2, expected]) => {
    const result = isSubsequence(str1, str2);
    console.log(`"${str1}" ? "${str2}": ${result} ${result === expected ? '?' : '?'}`);
});
"ace" ? "abcde": true ?
"aec" ? "abcde": false ?
"abc" ? "aabbcc": true ?
"axc" ? "ahbgdc": false ?
"" ? "abc": true ?
"abc" ? "": false ?

Time and Space Complexity

The algorithm has O(n + m) time complexity where n and m are the lengths of str1 and str2 respectively. Space complexity is O(1) as we only use two pointer variables.

Conclusion

The two-pointer approach efficiently determines if one string is a subsequence of another by maintaining character order. This technique is fundamental in dynamic programming and string manipulation problems.

Updated on: 2026-03-15T23:19:00+05:30

1K+ Views

Kickstart Your Career

Get certified by completing the course

Get Started
Advertisements