I am simplifying greatly, maybe someone else can provide a
more formal answer.
I see that homomorphic encryption works with only numeric data.
There are many ways to encode strings into integers,
you can encode a string into bytes and then translate these bytes to an integer.
I have read that homomorphic has scalability issues as it creates a 100x increase in encrypted data.
Homomorphic encryption indeed comes at a price both in size of the ciphertexts and efficiency of the computations.
Which one to use for which use-cases?
ZK-Snark is a zero-knowledge proof-of-knowledge: One party, the Prover, knows a secret and wants to convince another party, the Verifier, that a statement involving the secret is true, without revealing the secret.
Example: A user knows a password $x$ and a server knows a hash of the password $h$, the user needs to prove the statement:
I know a value $x$ such that $H(x)=h$
Homomorphic encryption is type of an encryption scheme: A value $x$ is transformed into a ciphertext $c=Enc_k(x)$ using a secret key $k$, such that $c$ doesn't reveal anything about $x$ without the key.
We say that such a scheme is homomorphic for a specific operation (I will design it by $\cdot$) if given $Enc_k(x_1)$ and $Enc_k(x_2)$, one can compute
$Enc_k(x_1\cdot x_2)$ without decrypting the ciphertexts.
We say it is fully homomorphic, if given encrypted inputs
$Enc_k(x_1), \dots Enc_k(x_n)$, one can compute for any arbitrary function $f$, the value $Enc_k(f(x_1,\dots, x_n))$ without decrypting the ciphertexts.
Use case: We know some secret value $x$, and want the value $f(x)$. Since $f$ is too computationally intensive, we need to delegate it to a worker (e.g. a cloud server) with more resources.
To avoid revealing $x$ to the worker, we can send $Enc_k(x)$ to the worker, which computes $f$ via homomorphic computations, and sends back $Enc_k(f(x))$. We can then decrypt $f(x) := Dec_k(Enc_k(f(x)))$.
Zk-snarks are usually used when the computation is realized by the party holding the secret, which is trusted to protect the secrets, but is not trusted to correctly execute the computation.
Homomorphic encryption is usually used when the computation is realized by a party that doesn't know the secrets, and is not trusted to protect these secrets, but is trusted to correctly execute the computation.
These two constructions could be combined if you want to make sure that the worker sends back $Enc_k(f(x))$ and not some other value $Enc_k(g(x'))$. Then we could require the worker
to send back a proof that the computation was done correctly.
But then we wouldn't need the zero-knowledge property, since the worker doesn't know any secret.